Merge pull request #4 from We-Dont-Byte/merge-johnbreaux-thomaslane

Merge "merge-johnbreaux-thomaslane" into "master"
This includes breaking changes!
See pull request #4 for list of changes.
This commit is contained in:
John Breaux 2022-05-07 21:16:29 -05:00 committed by GitHub
commit 0a39522eca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 2212 additions and 671 deletions

View File

@ -10,15 +10,22 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [macos-11, ubuntu-latest, windows-latest] os:
- macos-11
- ubuntu-latest
- windows-latest
node_version:
- 16
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Install Node.js - name: Install Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v3
with: with:
node-version: 10.x node-version: ${{ matrix.node_version }}
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
- run: npm ci - run: npm ci
- run: xvfb-run -a npm test - run: xvfb-run -a npm test
if: runner.os == 'Linux' if: runner.os == 'Linux'

1
.gitignore vendored
View File

@ -2,4 +2,5 @@ out
dist dist
node_modules node_modules
.vscode-test/ .vscode-test/
*.code-workspace
*.vsix *.vsix

View File

@ -3,7 +3,7 @@
<img alt="Mind Reader Logo" src="media/logo.png"></img> <img alt="Mind Reader Logo" src="media/logo.png"></img>
</p> </p>
<h1>Mind_Reader</h1> <h1>Mind Reader</h1>
<!-- overview description --> <!-- overview description -->
@ -42,7 +42,7 @@ Python programming with LEGO Mindstorms. Our goal is to:
<img width="50%" height="50%" alt="tools for native modules page with tool installation checked" src="media/nodejs_setup.png"></img> <img width="50%" height="50%" alt="tools for native modules page with tool installation checked" src="media/nodejs_setup.png"></img>
</p> </p>
If the compiled serial port version is incompatible, you may see no options presented in the Mind_Reader actions panel: If the compiled serial port version is incompatible, you may see no options presented in the Mind Reader actions panel:
<p align="center"> <p align="center">
<img width="50%" height="50%" alt="mind reader actions panel with no items:" src="media/missing_actions.png"></img> <img width="50%" height="50%" alt="mind reader actions panel with no items:" src="media/missing_actions.png"></img>
@ -73,7 +73,7 @@ The electron version should be listed, e.g.: `Electron: 13.5.2`
<img width="35%" height="35%" alt="vscode information" src="media/vscode_info.png"></img> <img width="35%" height="35%" alt="vscode information" src="media/vscode_info.png"></img>
</p> </p>
### 3 Finding the Mind_Reader extension directory ### 3 Finding the Mind Reader extension directory
On MacOS and Linux this is `~/.vscode/extensions`. On MacOS and Linux this is `~/.vscode/extensions`.
On Windows this is `C:\<YOUR USER>\.vscode\extensions\`. However, in Git Bash, it will appear like on MacOS and Linux On Windows this is `C:\<YOUR USER>\.vscode\extensions\`. However, in Git Bash, it will appear like on MacOS and Linux
@ -81,7 +81,7 @@ e.g.: `~/.vscode/extensions`.
--- ---
Find the Mind_Reader extension folder, this should look like `xxx.mind-reader-x.x.x`. Find the Mind Reader extension folder, this should look like `xxx.mind-reader-x.x.x`.
Navigate to the found folder in the terminal. Navigate to the found folder in the terminal.
@ -102,8 +102,8 @@ $ electron-rebuild --version=ELECTRON_VERSION
Use the following to set up the extension for development. Use the following to set up the extension for development.
```console ```console
$ git clone https://github.com/SingleSemesterSnobs/Mind_Reader.git $ git clone https://github.com/SingleSemesterSnobs/Mind Reader.git
$ cd Mind_Reader $ cd Mind Reader
$ npm install $ npm install
``` ```

View File

@ -8,7 +8,7 @@
<body> <body>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6a4XaqHkKcxJ6ZFms1RNrRurcOfl-diW90DAdpAx0Kv-rtrLJXovIhcUpayqFHATkrQ&usqp=CAU" width="600" /> <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6a4XaqHkKcxJ6ZFms1RNrRurcOfl-diW90DAdpAx0Kv-rtrLJXovIhcUpayqFHATkrQ&usqp=CAU" width="600" />
<p></p> <p></p>
<h1>Welcome to Mind_Reader!</h1> <h1>Welcome to Mind Reader!</h1>
<p>We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.</p> <p>We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.</p>
<ul> <ul>
<li> <li>

42
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "mind-reader", "name": "mind-reader",
"version": "0.0.1", "version": "2.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mind-reader", "name": "mind-reader",
"version": "0.0.1", "version": "2.0.0",
"dependencies": { "dependencies": {
"serialport": "^9.2.5" "serialport": "^9.2.5"
}, },
@ -26,7 +26,7 @@
"vscode-test": "^1.5.2" "vscode-test": "^1.5.2"
}, },
"engines": { "engines": {
"vscode": "^1.60.0" "vscode": "^1.66.0"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@ -2101,9 +2101,9 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"node_modules/mkdirp": { "node_modules/mkdirp": {
"version": "0.5.5", "version": "0.5.5",
@ -2817,9 +2817,9 @@
] ]
}, },
"node_modules/simple-get": { "node_modules/simple-get": {
"version": "3.1.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": { "dependencies": {
"decompress-response": "^4.2.0", "decompress-response": "^4.2.0",
"once": "^1.3.1", "once": "^1.3.1",
@ -3205,9 +3205,9 @@
} }
}, },
"node_modules/wide-align/node_modules/ansi-regex": { "node_modules/wide-align/node_modules/ansi-regex": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
"engines": { "engines": {
"node": ">=4" "node": ">=4"
} }
@ -4896,9 +4896,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.5", "version": "0.5.5",
@ -5405,9 +5405,9 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
}, },
"simple-get": { "simple-get": {
"version": "3.1.0", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"requires": { "requires": {
"decompress-response": "^4.2.0", "decompress-response": "^4.2.0",
"once": "^1.3.1", "once": "^1.3.1",
@ -5715,9 +5715,9 @@
}, },
"dependencies": { "dependencies": {
"ansi-regex": { "ansi-regex": {
"version": "3.0.0", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
}, },
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",

View File

@ -1,10 +1,56 @@
{ {
"name": "mind-reader", "name": "mind-reader",
"displayName": "Mind_Reader", "displayName": "Mind Reader",
"repository": "https://github.com/SingleSemesterSnobs/Mind_Reader", "homepage": "https://github.com/We-Dont-Byte/Mind_Reader/wiki",
"version": "1.0.0", "repository": {
"type": "git",
"url": "https://github.com/We-Dont-Byte/Mind_Reader"
},
"bugs": {
"type": "git",
"url": "https://github.com/We-Dont-Byte/Mind_Reader/issues"
},
"contributors": [
{
"name" : "Jake Grossman",
"email" : "JacobGrossman2@my.unt.edu"
},
{
"name" : "Cal Wooten",
"email" : "calwooten@my.unt.edu"
},
{
"name" : "Josiah Mosesn",
"email" : "josiahmoses@my.unt.edu"
},
{
"name" : "Sophia Drewfs",
"email" : "sophiadrewfs@my.unt.edu"
},
{
"name" : "John Breaux",
"email" : "JohnBreaux@my.unt.edu"
},
{
"name" : "Thomas Lane",
"email" : "ThomasLane2@my.unt.edu"
},
{
"name" : "Ryan Tolbert",
"email" : "RyanTolbert@my.unt.edu"
},
{
"name" : "Kendrick Johnson",
"email" : "KendrickJohnson@my.unt.edu"
},
{
"name" : "Pedro Alvarez",
"email" : "PedroAlvarez3@my.unt.edu"
}
],
"version": "2.0.0",
"engines": { "engines": {
"vscode": "^1.60.0" "vscode": "^1.66.0"
}, },
"categories": [ "categories": [
"Other" "Other"
@ -15,199 +61,249 @@
"main": "./out/extension.js", "main": "./out/extension.js",
"contributes": { "contributes": {
"commands": [ "commands": [
{
"command": "mind-reader.helloWorld",
"title": "Hello World"
},
{ {
"command": "mind-reader.increaseFontScale", "command": "mind-reader.increaseFontScale",
"title": "Increase Font Scale" "title": "Increase Font Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.decreaseFontScale", "command": "mind-reader.decreaseFontScale",
"title": "Decrease Font Scale" "title": "Decrease Font Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.resetFontScale", "command": "mind-reader.resetFontScale",
"title": "Reset Font Scale" "title": "Reset Font Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.increaseEditorScale", "command": "mind-reader.increaseEditorScale",
"title": "Increase Editor Scale" "title": "Increase Editor Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.decreaseEditorScale", "command": "mind-reader.decreaseEditorScale",
"title": "Decrease Editor Scale" "title": "Decrease Editor Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.resetEditorScale", "command": "mind-reader.resetEditorScale",
"title": "Reset Editor Scale" "title": "Reset Editor Scale",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.selectTheme", "command": "mind-reader.selectTheme",
"title": "Select Theme" "title": "Select Theme",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.openWebview", "command": "mind-reader.openWebview",
"title": "Mind Reader Webview" "title": "Mind Reader Webview"
}, },
{ {
"command": "mind-reader.openKeyBindWin", "command": "mind-reader.openKeybinds",
"title": "Key Bindings for Windows" "title": "Edit Keybinds",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.openKeyBindMac", "command": "mind-reader.getLineScope",
"title": "Key Bindings for Mac" "title": "Get Scope of the Current Line",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.runLineContext", "command": "mind-reader.getWordsUnderCursor",
"title": "Run Line Context" "title": "Get Words Under the Cursor",
"category": "Mind Reader"
}, },
{ {
"command": "mind-reader.runCursorContext", "command": "mind-reader.getLineNumber",
"title": "Run Cursor Context" "title": "Get The Current Line Number"
}, },
{ {
"command": "mind-reader.getIndent", "command": "mind-reader.getIndent",
"title": "Get Line Indentation" "title": "Get The Number Of Line Indentations"
},
{
"command": "mind-reader.getLeadingSpaces",
"title": "Get The Number Of Leading Spaces"
},
{
"command": "mind-reader.selectLeadingWhitespace",
"title": "Select The Leading Whitespace"
},
{
"command": "mind-reader.getNumberOfSelectedLines",
"title": "Get The Number Of Selected Lines"
}, },
{ {
"command": "mind-reader.connectHub", "command": "mind-reader.connectHub",
"title": "Connect LEGO Hub" "title": "Connect LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
}, },
{ {
"command": "mind-reader.disconnectHub", "command": "mind-reader.disconnectHub",
"title": "Disconnect LEGO Hub" "title": "Disconnect LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
}, },
{ {
"command": "mind-reader.uploadCurrentFile", "command": "mind-reader.uploadCurrentFile",
"title": "Upload current file to LEGO Hub" "title": "Upload Current File to LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
}, },
{ {
"command": "mind-reader.runProgram", "command": "mind-reader.runProgram",
"title": "Run a program from the LEGO Hub" "title": "Run a Program on the LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
}, },
{ {
"command": "mind-reader.stopExecution", "command": "mind-reader.stopExecution",
"title": "Stop running program on the LEGO Hub" "title": "Stop Running Program on the LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
}, },
{ {
"command": "mind-reader.deleteProgram", "command": "mind-reader.deleteProgram",
"title": "Delete a program from the LEGO Hub" "title": "Delete a Program from the LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
},
{
"command": "mind-reader.uploadCurrentFile",
"title": "Upload Current File to the LEGO SPIKE Prime Hub",
"category": "SPIKE Prime"
},
{
"command": "mind-reader.getLeadingSpaces",
"title": "Get Leading Spaces",
"category": "Mind Reader"
},
{
"command": "mind-reader.getLineNumber",
"title": "Get Line Number",
"category": "Mind Reader"
},
{
"command": "mind-reader.getQuickInputBack",
"title": "Go Back in Quick Input",
"category": "Mind Reader"
},
{
"command": "mind-reader.gotoLine",
"title": "Go to Line",
"category": "Mind Reader"
},
{
"command": "mind-reader.gotoSymbol",
"title": "Go to Symbol",
"category": "Mind Reader"
},
{
"command": "mind-reader.navigateBack",
"title": "Navigate Backward",
"category": "Mind Reader"
},
{
"command": "mind-reader.navigateForward",
"title": "Navigate Forward",
"category": "Mind Reader"
},
{
"command": "mind-reader.nextInFiles",
"title": "Go to Next Problem in Files (Error, Warning, Info)",
"category": "Mind Reader"
},
{
"command": "mind-reader.prevInFiles",
"title": "Go to Previous Problem in Files (Error, Warning, Info)",
"category": "Mind Reader"
},
{
"command": "mind-reader.quickOpen",
"title": "Go to File...",
"category": "Mind Reader"
},
{
"command": "mind-reader.quickOpenPreviousRecentlyUsedEditorInGroup",
"title": "View: Quick Open Previous Recently Used Editor in Group"
},
{
"command": "mind-reader.showAllSymbols",
"title": "Go to Symbol in Workspace...",
"category": "Mind Reader"
},
{
"command": "mind-reader.showProblems",
"title": "Show Problems",
"category": "Mind Reader"
},
{
"command": "mind-reader.showCommands",
"title": "Show All Commands",
"category": "Mind Reader"
} }
], ],
"keybindings": [ "keybindings": [
{ {
"command": "mind-reader.decreaseFontScale", "command": "editor.action.fontZoomOut",
"key": "numpad_subtract", "key": "Shift+Alt+z -",
"mac": "d" "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
}, },
{ {
"command": "mind-reader.increaseFontScale", "command": "editor.action.fontZoomIn",
"key": "numpad_add", "key": "Shift+Alt+z =",
"mac": "[NumpadAdd]" "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
}, },
{ {
"command": "mind-reader.increaseEditorScale", "command": "editor.action.fontZoomReset",
"key": "shift+numpad_add", "key": "Shift+Alt+z 0",
"mac": "Shift+[NumpadAdd]" "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
},
{
"command": "mind-reader.decreaseEditorScale",
"key": "shift+numpad_subtract",
"mac": "Shift+[NumpadSubtract]"
},
{
"command": "mind-reader.resetEditorScale",
"key": "shift+enter",
"mac": "Shift+[Enter]"
},
{
"command": "mind-reader.showAllSymbols",
"key": "Ctrl+T",
"mac": "Cmd+[KeyT]"
},
{
"command": "mind-reader.gotoLine",
"key": "CTRL+G",
"mac": "Cmd+[KeyG]"
},
{
"command": "mind-reader.quickOpen",
"key": "CTRL+P",
"mac": "Cmd+[KeyP]"
},
{
"command": "mind-reader.gotoSymbol",
"key": "Ctrl+Shift+O",
"mac": "Cmd+Shift+[KeyO]"
},
{
"command": "mind-reader.showProblems",
"key": "Ctrl+Shift+M",
"mac": "Cmd+Shift+[KeyM]"
},
{
"command": "mind-reader.nextInFiles",
"key": "F8",
"mac": "[F8]"
},
{
"command": "mind-reader.prevInFiles",
"key": "Shift+F8",
"mac": "Shift+[F8]"
},
{
"command": "mind-reader.quickOpenPreviousRecentlyUsedEditorInGroup",
"key": "Ctrl+Tab",
"mac": "Cmd+[Tab]"
},
{
"command": "mind-reader.navigateBack",
"key": "Ctrl+Alt+-",
"mac": "Cmd+Alt+[Minus]"
},
{
"command": "mind-reader.getQuickInputBack",
"key": "Ctrl+Alt+-",
"mac": "Cmd+Alt+[Minus]"
},
{
"command": "mind-reader.navigateForward",
"key": "Ctrl+Shift+-",
"mac": "Cmd+Shift+[Minus]"
},
{
"command": "mind-reader.selectTheme",
"key": "Ctrl+Shift+1",
"mac": "Cmd+Shift+[Digit1]"
}, },
{ {
"command": "mind-reader.getIndent", "command": "mind-reader.getIndent",
"key": "Shift+Tab", "key": "Ctrl+Shift+/ I",
"mac": "Shift+[Tab]" "mac": "Cmd+Shift+[Slash] I",
"when": "editorTextFocus"
}, },
{ {
"command": "mind-reader.openKeyBindWin", "command": "mind-reader.getLeadingSpaces",
"key": "Ctrl+Shift+8", "key": "Ctrl+Shift+/ S",
"mac": "Cmd+Shift+8" "mac": "Cmd+Shift+[Slash] S",
"when": "editorTextFocus",
"comment": "Requires python language"
}, },
{ {
"command": "mind-reader.openKeyBindMac", "command": "mind-reader.getLineNumber",
"key": "Ctrl+Shift+9", "key": "Ctrl+Shift+/ L",
"mac": "Cmd+Shift+9" "mac": "Cmd+Shift+[Slash] L",
"when": "editorTextFocus"
},
{
"command": "mind-reader.getLineScope",
"key": "Ctrl+Shift+/ C",
"mac": "Cmd+Shift+[Slash] C",
"when": "editorTextFocus && editorLangId == python"
},
{
"command": "mind-reader.openKeybinds",
"key": "Ctrl+Shift+/ K",
"mac": "Cmd+Shift+[Slash] K"
},
{
"command": "mind-reader.ev3.test",
"key": "Ctrl+E Ctrl+V"
} }
], ],
"menus": { "menus": {
"editor/context": [ "editor/context": [{
{
"submenu": "mind-reader.editor.context", "submenu": "mind-reader.editor.context",
"group": "mind-reader" "group": "mind-reader"
} }],
],
"mind-reader.editor.context": [ "mind-reader.editor.context": [
{ {
"command": "mind-reader.increaseEditorScale", "command": "mind-reader.selectTheme",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.increaseFontScale",
"group": "mind-reader", "group": "mind-reader",
"when": "activeEditor" "when": "activeEditor"
}, },
@ -237,7 +333,27 @@
"when": "activeEditor" "when": "activeEditor"
}, },
{ {
"command": "mind-reader.selectTheme", "command": "mind-reader.getLineNumber",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.getIndent",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.getLeadingSpaces",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.selectLeadingWhitespace",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.getNumberOfSelectedLines",
"group": "mind-reader", "group": "mind-reader",
"when": "activeEditor" "when": "activeEditor"
}, },
@ -247,30 +363,25 @@
"when": "activeEditor" "when": "activeEditor"
}, },
{ {
"command": "mind-reader.openKeyBindWin", "command": "mind-reader.openKeybinds",
"group": "mind-reader",
"when": "activeEditor"
},
{
"command": "mind-reader.openKeyBindMac",
"group": "mind-reader", "group": "mind-reader",
"when": "activeEditor" "when": "activeEditor"
} }
] ]
}, },
"submenus": [ "submenus": [{
{
"id": "mind-reader.editor.context", "id": "mind-reader.editor.context",
"label": "Mind_Reader" "label": "Mind Reader"
} }],
], "configuration": [{
"configuration": { "title": "Mind Reader",
"title": "Mind_Reader", "order": 0,
"properties": { "properties": {
"mindReader.productType": { "mind-reader.productType": {
"order": 1,
"type": "string", "type": "string",
"description": "Specifies the LEGO® product.", "description": "Specifies the LEGO® product.",
"default": "MINDSTORMS EV3", "default": "SPIKE Prime",
"enum": [ "enum": [
"MINDSTORMS EV3", "MINDSTORMS EV3",
"SPIKE Prime" "SPIKE Prime"
@ -280,40 +391,177 @@
"LEGO® Education SPIKE™ Prime Set (45678)" "LEGO® Education SPIKE™ Prime Set (45678)"
] ]
}, },
"mindReader.reader.screenReader": { "mind-reader.reader.screenReader": {
"order": 0,
"type": "string", "type": "string",
"description": "Specifies which screen reader to optimize for.", "description": "Specifies which screen reader to optimize for.",
"default": "NVDA", "default": "NVDA",
"enum": [ "enum": [
"JAWS",
"NVDA", "NVDA",
"Orca", "Orca",
"VoiceOver" "VoiceOver"
], ],
"enumDescriptions": [ "enumDescriptions": [
"Job Access With Speech (Windows)",
"NonVisual Desktop Access (Windows)", "NonVisual Desktop Access (Windows)",
"Orca (Linux)", "Orca (Linux)",
"Apple VoiceOver (macOS)" "Apple VoiceOver (macOS)"
] ]
}, },
"mindReader.reader.contextWindow": { "mind-reader.reader.contextWindow": {
"order": 3,
"type": "number", "type": "number",
"description": "The number of words around the cursor to use when reading the cursor context", "description": "The number of words around the cursor to use when reading the cursor context",
"default": 1 "default": 1
}, },
"mindReader.connection.portPath": { "mind-reader.connection.portPath": {
"order": 2,
"type": "string", "type": "string",
"markdownDescription": "The default port to try and establish a connection on." "markdownDescription": "The default port to try and establish a connection on."
},
"mindReader.connection.clearOutputOnRun": {
"type": "boolean",
"description": "Whether to clear the output each time the program is run",
"default": "true"
} }
} }
}, },
"views": {
"MindReader": [
{ {
"title": "Line Highlighter [Must Close Settings And RESTART VSCode For The Line Highlighter To Function]",
"order": 1,
"properties": {
"mind-reader.lineHighlighter.isEnabled": {
"order": 4,
"type": "boolean",
"markdownDescription": "Enable/Disable the line highlighter.\n\n\n* `Enabled (our default)`: Checked: Line Highlighter is turned `ON`\n* `Disabled`: Unchecked: Line Highlighter is turned `OFF`\n\n### `NOTE`: You Must Close Settings And RESTART Visual Studio Code For The Line Highlighter To Function\n#### Even If No Changes Were Made",
"default": true
},
"mind-reader.lineHighlighter.multiLineIsEnabled": {
"order": 5,
"type": "boolean",
"markdownDescription": "Temporarily Disable highlighting when highlighting multiple lines.\n\n\n* `Enabled`: Checked: Multiline Highlighting is turned `ON`:\n* * When you click and drag line highlights will be applied to all lines\n* `Disabled (our default)`: Unchecked: Multiline Highlighting is turned `OFF`:\n* * When you click and drag the line highlighter will disable itself then re-enable itself when you click onto a single line.",
"default": false
},
"mind-reader.lineHighlighter.backgroundColor": {
"order": 6,
"type": "string",
"markdownDescription": "Set the background color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#232C5C` is our default",
"default": "#232C5C"
},
"mind-reader.lineHighlighter.outlineColor": {
"order": 7,
"type": "string",
"markdownDescription": "Set the outline color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#4866FE` is our default",
"default": "#4866FE"
},
"mind-reader.lineHighlighter.outlineWidth": {
"order": 8,
"type": "string",
"markdownDescription": "Set the outline width to be used by the line highlighter.\n\nSyntax: `medium` or `thin` or `thick` or `length` or `none`\n* `medium`: Specifies a medium outline. (usual default)\n* `thin`: Specifies a thin outline\n* `thick`: Specifies a thick outline\n* `length`: you to define the thickness of the outline in [length](https://www.w3schools.com/cssref/css_units.asp) units.\n* `none`: No outline width will be applied\n* `1px` is our default",
"default": "1px"
},
"mind-reader.lineHighlighter.outlineStyle": {
"order": 9,
"type": "string",
"markdownDescription": "Set the outline style to be used by the line highlighter.\n\nSyntax: `none` or `hidden` or `dotted` or `dashed` or `solid` or `double` or `groove` or `ridge` or `inset` or `outset`\n* `none`: No border will be applied (usual default)\n* `hidden`: The same as `none`\n* `dotted`: Dotted border\n* `dashed`: Dashed border\n* `solid (our default)`: Solid border\n* `double`: Double border\n* `groove`: 3D grooved border, depends on the outline-color value.\n* `ridge`: 3D ridged border, depends on the outline-color value.\n* `inset`: 3D inset border, depends on the outline-color value.\n* `outset`: 3D outset border, depends on the outline-color value.",
"default": "solid"
},
"mind-reader.lineHighlighter.borderColorTop": {
"order": 10,
"type": "string",
"markdownDescription": "Set the top border color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#FFFFFF` is our default",
"default": "#FFFFFF"
},
"mind-reader.lineHighlighter.borderColorRight": {
"order": 11,
"type": "string",
"markdownDescription": "Set the right border color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#FFFFFF` is our default",
"default": "#FFFFFF"
},
"mind-reader.lineHighlighter.borderColorBottom": {
"order": 12,
"type": "string",
"markdownDescription": "Set the bottom border color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#FFFFFF` is our default",
"default": "#FFFFFF"
},
"mind-reader.lineHighlighter.borderColorLeft": {
"order": 13,
"type": "string",
"markdownDescription": "Set the left border color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#FFFFFF` is our default",
"default": "#FFFFFF"
},
"mind-reader.lineHighlighter.borderWidthTop": {
"order": 14,
"type": "string",
"markdownDescription": "Set the top border width to be used by the line highlighter.\n\nSyntax: `medium` or `thin` or `thick` or `length` or `none`\n* `medium`: Specifies a medium border. (usual default)\n* `thin`: Specifies a thin border\n* `thick`: Specifies a thick border\n* `length`: you to define the thickness of the border in [length](https://www.w3schools.com/cssref/css_units.asp) units.\n* `none`: No border width will be applied\n* `1px` is our default",
"default": "1px"
},
"mind-reader.lineHighlighter.borderWidthRight": {
"order": 15,
"type": "string",
"markdownDescription": "Set the right border width to be used by the line highlighter.\n\nSyntax: `medium` or `thin` or `thick` or `length` or `none`\n* `medium`: Specifies a medium border. (usual default)\n* `thin`: Specifies a thin border\n* `thick`: Specifies a thick border\n* `length`: you to define the thickness of the border in [length](https://www.w3schools.com/cssref/css_units.asp) units.\n* `none`: No border width will be applied\n* `16px` is our default\n\n#### \nborderWidthRight exhibits odd behavior, play around with different sizes to find what works best for your environment.",
"default": "16px"
},
"mind-reader.lineHighlighter.borderWidthBottom": {
"order": 16,
"type": "string",
"markdownDescription": "Set the bottom border width to be used by the line highlighter.\n\nSyntax: `medium` or `thin` or `thick` or `length` or `none`\n* `medium`: Specifies a medium border. (usual default)\n* `thin`: Specifies a thin border\n* `thick`: Specifies a thick border\n* `length`: you to define the thickness of the border in [length](https://www.w3schools.com/cssref/css_units.asp) units.\n* `none`: No border width will be applied\n* `1px` is our default",
"default": "1px"
},
"mind-reader.lineHighlighter.borderWidthLeft": {
"order": 17,
"type": "string",
"markdownDescription": "Set the left border width to be used by the line highlighter.\n\nSyntax: `medium` or `thin` or `thick` or `length` or `none`\n* `medium`: Specifies a medium border. (usual default)\n* `thin`: Specifies a thin border\n* `thick`: Specifies a thick border\n* `length`: you to define the thickness of the border in [length](https://www.w3schools.com/cssref/css_units.asp) units.\n* `none`: No border width will be applied\n* `1px` is our default",
"default": "1px"
},
"mind-reader.lineHighlighter.borderStyleTop": {
"order": 18,
"type": "string",
"markdownDescription": "Set the top border style to be used by the line highlighter.\n\nSyntax: `none` or `hidden` or `dotted` or `dashed` or `solid` or `double` or `groove` or `ridge` or `inset` or `outset`\n* `none`: No border will be applied (usual default)\n* `hidden`: The same as `none`\n* `dotted`: Dotted border\n* `dashed`: Dashed border\n* `solid (our default)`: Solid border\n* `double`: Double border\n* `groove`: 3D grooved border, depends on the border-color value.\n* `ridge`: 3D ridged border, depends on the border-color value.\n* `inset`: 3D inset border, depends on the border-color value.\n* `outset`: 3D outset border, depends on the border-color value.",
"default": "solid"
},
"mind-reader.lineHighlighter.borderStyleRight": {
"order": 19,
"type": "string",
"markdownDescription": "Set the right border style to be used by the line highlighter.\n\nSyntax: `none` or `hidden` or `dotted` or `dashed` or `solid` or `double` or `groove` or `ridge` or `inset` or `outset`\n* `none`: No border will be applied (usual default)\n* `hidden`: The same as `none`\n* `dotted`: Dotted border\n* `dashed`: Dashed border\n* `solid (our default)`: Solid border\n* `double`: Double border\n* `groove`: 3D grooved border, depends on the border-color value.\n* `ridge`: 3D ridged border, depends on the border-color value.\n* `inset`: 3D inset border, depends on the border-color value.\n* `outset`: 3D outset border, depends on the border-color value.",
"default": "solid"
},
"mind-reader.lineHighlighter.borderStyleBottom": {
"order": 20,
"type": "string",
"markdownDescription": "Set the bottom border style to be used by the line highlighter.\n\nSyntax: `none` or `hidden` or `dotted` or `dashed` or `solid` or `double` or `groove` or `ridge` or `inset` or `outset`\n* `none`: No border will be applied (usual default)\n* `hidden`: The same as `none`\n* `dotted`: Dotted border\n* `dashed`: Dashed border\n* `solid (our default)`: Solid border\n* `double`: Double border\n* `groove`: 3D grooved border, depends on the border-color value.\n* `ridge`: 3D ridged border, depends on the border-color value.\n* `inset`: 3D inset border, depends on the border-color value.\n* `outset`: 3D outset border, depends on the border-color value.",
"default": "solid"
},
"mind-reader.lineHighlighter.borderStyleLeft": {
"order": 21,
"type": "string",
"markdownDescription": "Set the left border style to be used by the line highlighter.\n\nSyntax: `none` or `hidden` or `dotted` or `dashed` or `solid` or `double` or `groove` or `ridge` or `inset` or `outset`\n* `none`: No border will be applied (usual default)\n* `hidden`: The same as `none`\n* `dotted`: Dotted border\n* `dashed`: Dashed border\n* `solid (our default)`: Solid border\n* `double`: Double border\n* `groove`: 3D grooved border, depends on the border-color value.\n* `ridge`: 3D ridged border, depends on the border-color value.\n* `inset`: 3D inset border, depends on the border-color value.\n* `outset`: 3D outset border, depends on the border-color value.",
"default": "solid"
},
"mind-reader.lineHighlighter.fontStyle": {
"order": 22,
"type": "string",
"markdownDescription": "Set the font style to be used by the line highlighter.\n\nSyntax: `normal` or `italic` or `oblique` or `none`\n* `normal (our default)`: Displays a normal font style. This is default\n* `italic`: Displays an italic font style\n* `oblique`: Displays an oblique font style\n* `none`: No font style will be applied",
"default": "normal"
},
"mind-reader.lineHighlighter.fontWeight": {
"order": 23,
"type": "string",
"markdownDescription": "Set the font weight to be used by the line highlighter.\n\nSyntax: `normal` or `bold` or `bolder` or `lighter` or _number_ or `none`\n* `normal`: Normal Characters. (usual default)\n* `bold`: Thick Characters\n* `bolder (our default)`: Thicker Characters\n* `lighter`: Lighter Characters\n * _number_: From `thin` to `thick` characters: `100`, `200`, `300`, `400`, `500`, `600`, `700`, `800`, or `900`: `400` is the same as normal, and `700` is the same as bold.\n* `none`: No font weight will be applied",
"default": "bolder"
},
"mind-reader.lineHighlighter.textDecoration": {
"order": 24,
"type": "string",
"markdownDescription": "Set the text decoration to be used by the line highlighter.\n\nSyntax: `(text-decoration-line)` `(text-decoration-color)` `(text-decoration-style)` `(text-decoration-thickness)`\n* `text-decoration-line (required)`: Sets the kind of text decoration to use: `underline`, `overline`, `line-through`\n* `text-decoration-color`: Sets the color of the text decoration\n* `text-decoration-style`: Sets the style of the text decoration: `solid`, `wavy`, `dotted`, `dashed`, `double`\n* `text-decoration-thickness`: Sets the thickness of the decoration line\n* `none (our default)`: No text decorations will be applied\n\n`Examples`:\n1. underline blue wavy 5px\n2. line-through\n3. underline overline dotted red",
"default": "none"
},
"mind-reader.lineHighlighter.textColor": {
"order": 25,
"type": "string",
"markdownDescription": "Set the text color to be used by the line highlighter.\n\nSyntax: _color_ or `transparent`\n\nAvailable color formats include:\n* `HEX(A)`: for Hexadecimal Colors: `#RRGGBB` or `#RRGGBBAA` to add transparency\n* `RGB(A)`: for RGB Colors: `rgb(red, green, blue)` or `rgba(red, green, blue, alpha)`\n* `HSL(A)`: for HSL Colors: `hsl(hue, saturation, lightness)` or `hsla(hue, saturation, lightness, alpha)`\n* `Predefined Color Names`: 140 color names are predefined in the HTML and CSS color specification: `blue`, `red`, `coral`, `brown`, [etc...](https://www.w3schools.com/colors/colors_names.asp)\n* `None`: For no color to be applied: Sometimes VSCode will pull a color from your theme, other times it uses black\n* `#FFFFFF` is our default",
"default": "#FFFFFF"
}
}
}],
"views": {
"MindReader": [{
"id": "accessActions", "id": "accessActions",
"name": "Access Actions", "name": "Access Actions",
"icon": "media/dep.svg", "icon": "media/dep.svg",
@ -328,13 +576,11 @@
] ]
}, },
"viewsContainers": { "viewsContainers": {
"activitybar": [ "activitybar": [{
{
"id": "MindReader", "id": "MindReader",
"title": "MindReader Actions", "title": "MindReader Actions",
"icon": "media/dep.svg" "icon": "media/dep.svg"
} }]
]
} }
}, },
"scripts": { "scripts": {

View File

@ -0,0 +1,65 @@
#!/bin/bash
#* linux-install.sh: First-run setup script
#* Ensures git is installed, clones the repo, and then runs
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
ELEVATE='';if (( $UID !=0 )); then ELEVATE='sudo';fi
help () {
echo "Usage: $0 [-d] [-g path/to/git/directory]"
exit 0
}
gitdir=~/git
# Get option flags:
dry=false
while getopts ghd arg; do
case $arg in
g) gitdir="$OPTARG";;
h) help;;
d) dry=true;;
esac
done
function dryrun {
if $dry; then
echo "> $* [dry]";
else
echo "> $*"
$@
fi
}
setupdir="Mind_Reader/setup-development/linux"
repouri="https://github.com/We-Dont-Byte/Mind_Reader.git"
# Install git
if which git; then
echo "Git already installed."
elif which pacman; then
# using pacman
dryrun $ELEVATE pacman -Sy git
elif which apt; then
# using apt
dryrun $ELEVATE apt-get update && \
dryrun $ELEVATE apt-get install git -y
fi #? TODO: other package managers?
printf "\nCloning repository into $gitdir\n"
dryrun mkdir "$gitdir"
cd $gitdir && dryrun git clone "$repouri"
# TODO: remove this when merging!
cd Mind_Reader
dryrun git checkout johnBreaux
# TODO: remove this when merging!
cd "$gitdir/$setupdir"
bash ./upgrade-linux.sh $@
echo "Opening VS Code..."
cd $gitdir/Mind_Reader
code .

View File

@ -0,0 +1,4 @@
apt-transport-https
build-essential
python3
wget

View File

@ -0,0 +1,4 @@
base-devel
git
wget
python3

View File

@ -0,0 +1,141 @@
#!/bin/bash
#* linux-update.sh: Install and update dependencies of Mind Reader, on linux.
#* Heads-up, this expects to be run from Mind_Reader/setup-development/linux.
# If run with bash -vx, print useful information instead of just a + sign
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
# If run as root, it could be because sudo isn't installed (some people disagree with sudo, especially on Arch)
ELEVATE='';if (( $UID !=0 )); then ELEVATE='sudo';fi
# Get option flags:
dry=false
while getopts d arg; do
case $arg in
d) dry=true;;
esac
done
function dryrun {
if $dry; then
echo "> $* [dry]";
else
echo "> $*"
$@
fi
}
# Get whether the user is running in Windows Subsystem for Linux
function getwsl {
grep "[Mm]icrosoft" /proc/version > /dev/null
return $?
}
# Get the user's default login shell
function getsh {
#* This code was created by user [Todd A. Jacobs](https://stackoverflow.com/users/1301972/todd-a-jacobs) on [StackOverflow](https://stackoverflow.com/a/11059152) and is used in accordance with Creative Commons CC BY-SA 3.0
getent passwd $LOGNAME | cut -d: -f7
}
# Install NVM (this is gross, but the recommended way to install nvm)
function installnvm {
# nvm's install script tries to be smart, so we have to work around its supposed cleverness
usershell=`getsh`
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | dryrun "$usershell"
# Reload profile
case $usershell in
*/bash) dryrun . ~/.bashrc ~/.bashprofile;;
*/zsh) dryrun . ~/.zshrc;;
*) "Your shell, $usershell, is currently unsupported by nvm. It's up to you to set up your development environment."; exit;;
esac
}
# Set these variables if you need to install for a different architecture
# Valid architectures are "x64", "arm64", "armhf"
arch=""
case `uname -i` in
"x86_64") arch="x64";;
"armv[6-8]*") arch="armhf";;
"aarch64") arch="arm64";;
*) echo "Architecture '$(uname -i)' unknown. Assuming x86_64..."
arch="x64";;
esac
if which pacman; then
# Install dependencies with pacman
printf "Installing dependencies with pacman...\n"
cat ./package-managers/pacman.dependencies | dryrun $ELEVATE pacman -S --needed -
# If not in Windows Subsystem for Linux, install vscode
[[ !(getwsl) ]] && dryrun $ELEVATE pacman -S --needed code
# Install Node Version Manager
installnvm
elif which apt-get; then
# Install dependencies using apt-get
printf "Installing dependencies with apt...\n"
dryrun xargs -a ./package-managers/apt.dependencies $ELEVATE apt-get install -y
# Check if vscode exists, if not, install it.
# Microsoft doesn't put it in any Ubuntu repos, you have to get it straight from them.
# This does have the side effect, however, of installing the official repository
# Don't attempt to install vscode if running in WSL; it can cause problems.
if !(which code) && !(getwsl); then
#* Install VSCode
vscodepackagename="code_amd64.deb"
dryrun wget "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-$arch" -O ./code.deb
dryrun $ELEVATE apt install ./code.deb
dryrun rm ./code.deb
fi
# Install Node Version Manager (nvm)
installnvm
fi
cdir=$(pwd)
# Go back to source tree root
cd ../..
# Check the VSCode version
nodeversion="node"
electronversion=""
#* Note:
#* When adding support for new VSCode versions, update this case
#* By the time you're working on this project, things are likely going to differ!
case `code --version` in
#* Each version of VSCode has a corresponding Electron version and Node version
#* These are used when configuring nvm
1.66.*) electronversion="17.2.0"; nodeversion="16.13.0";;
1.67.*) electronversion="17.4.1"; nodeversion="16.13.0";;
*) nodeversion="--lts";;
esac
# Install NodeJS and npm
printf "\nInstalling node $nodeversion\n"
dryrun nvm install "$nodeversion"
dryrun nvm use "$nodeversion"
# Use npm to install electron-rebuild and yo
printf "Installing electron-rebuild, yo, and generator-code\n"
dryrun npm install electron-rebuild yo generator-code
# use npm to acquire dependencies for Mind-Reader
printf "\nAcquiring dependencies...\n"
dryrun npm install
# automatically update vulnerable packages, if possible
printf "\nUpdating vulnerable packages, if possible...\n"
dryrun npm audit fix
# Use electron-rebuild to rebuild electron
if [[ "$electronversion" != "" ]]; then
printf "\nRebuilding electron with version $electronversion...\n"
dryrun electron-rebuild --version $electronversion
else
printf "\n%s\n%s\n%s\n%s\n" \
"Open Visual Studio Code, select the 'Help' tab in the toolbar, and go to 'About'." \
"Find the line that says 'Electron: [electron version]'" \
"Run the command below, filling in the Electron version with the one from that menu:" \
"electron-rebuild --version [electron version]"
fi
cd $cdir
echo "Done!"

View File

@ -0,0 +1,179 @@
<#
.synopsis
Dependency installer for Mind Reader on Windows.
This sets up a development environment from a BARE windows install.
.description
Install Git for Windows, clone the Mind Reader repository, and install all dependencies.
The script uses winget (A.K.A. "App Installer") to download and install the latest versions of each dependency, defined in winget/dependencies.json
Winget comes preinstalled on Windows 11 (21H2)/10 (21H1) or newer, and can be installed on Windows 10 1704+ through the Windows Store.
If you download Microsoft's developer VM, you have it!
As WinGet is built into Windows, it sidesteps any annoying third-party package managers, and is the lowest common denominator for package installation.
.link
https://github.com/We-Dont-Byte/Mind_Reader/
.parameter GitDir
Path to clone the git repo into (Default: $HOME/git/)
.parameter AllowAdministrator
Force-allow running this script as Administrator (not recommended, despite the frequent UAC prompts!)
.parameter NoPrompt
Disable all prompts for user input, and all waiting. (not recommended when combined with AllowAdministrator!)
.parameter ForceInstall
Force installation/upgrade of all modules, even if already present on the system.
.parameter DryRun
Perform a "dry run" of the script, changing directories and running commands, but without modifying anything.
.example
./install-windows.ps1
Perform a default upgrade of all Mind Reader dependencies
.example
./install-windows.ps1 -DryRun
Perform a dry run of the upgrade process, so you can evaluate what commands will be run
.example
./install-windows.ps1 -NoPrompt
Don't prompt for user input when upgrading
.example
./install-windows.ps1 AllowAdministrator
Allow script to run as Administrator
#>
param (
[string]$GitDir = "$HOME/git/", # Path to clone the git repo into
[switch]$h, [switch]$Help, # Get help
[switch]$AllowAdministrator, # Force allow installation as administrator
[switch]$NoPrompt, # Disable the 3-second wait and press-any-key prompt
[switch]$ForceInstall, # Always try to install
[switch]$DryRun # Run script without installing
)
$RepoURI = "https://github.com/We-Dont-Byte/Mind_Reader.git"
$RepoPath = "$GitDir\Mind_Reader"
$SetupPath = "$RepoPath\setup-development\windows"
if ($h -or $Help) {
Get-Help ./install-windows.ps1
exit
}
# .description
# Get-CommandAvailable: Checks whether a given command is available.
# If command is available, returns $false
function Get-CommandAvailable {
param ($command)
# Use a wildcard here so the command doesn't throw an exception we'd have to trycatch
# It's not a filthy hack if it's elegant!
RETURN (Get-Command -Name $command*)
}
#.description
# Invoke-DryRun a powershell statement
function Invoke-DryRun {
param ([string] $command)
$prompt = "> "
if ($DryRun) {
Write-Host "$prompt$command [dry]" -ForegroundColor darkgray
}
else {
Write-Host "$prompt$command" -ForegroundColor white
Invoke-Expression $command
}
}
#.description
# Reset-Path: Reload the Path environment variable
function Reset-Path {
Write-Output "Reloading Path..."
#* This code was created by user [mpen](https://stackoverflow.com/users/65387/mpen) on [StackOverflow](https://stackoverflow.com/a/31845512) and is used in accordance with Creative Commons CC BY-SA 3.0
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
}
# Check if Winget is available
if ( -not (Get-CommandAvailable winget) ) {
Write-Warning "It looks like winget isn't available.`n"
Write-Host "Update 'App Installer' through the Microsoft Store, or grab the '.msixbundle' from the winget-cli repository:"
Write-Host "( https://github.com/microsoft/winget-cli/releases/latest )`n" -ForegroundColor White
exit
}
# Check if the user ran the script with administrator privileges.
# Warn them.
if ( ([Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544') ) {
# If an administrator requests installation as administator,
# for example, to keep UAC prompts to a minimum, allow it.
if ($AllowAdministrator) {
Write-Warning "Script was run as Administrator. Exit now if you didn't mean to do this!"
# If you pass NoPrompt as an arg, you're very aware of the damage this script could do to your build env, and you just don't care
# The true chad of sysadmins.
if (!$NoPrompt) {
for ( $i = 3; $i -gt 0; $i--) {
Write-Host "Press Ctrl+C to exit. Continuing in $i...`r" -NoNewLine
Start-Sleep 1
}
Write-Host "Press any key to continue... "
[void][Console]::ReadKey(1) # Equivalent to Command Prompt's `pause` command
}
} else {
# Throw a fatal error if the user tries to run as administrator.
Throw "Script must be run as a normal user."
}
}
# Install Git
if ( -not (Get-CommandAvailable git) ) {
Write-Host "`nInstalling Git with winget..."
Invoke-DryRun 'winget install --id Git.Git'
Reset-Path
if ( -not (Get-CommandAvailable git)) {
Throw "Git failed to install. Aborting."
}
} else {
Write-Host "Git already installed." -ForegroundColor green
}
# Create git directory in GitDir
if ( -not (Test-Path "$GitDir") ) {
Invoke-DryRun "mkdir '$GitDir'"
}
# Clone the repository in GitDir
$dir = $pwd
Set-Location $GitDir
Invoke-DryRun "git clone '$RepoURI'"
# TODO: Remove this when merging
Set-Location Mind_reader
Invoke-DryRun "git checkout johnBreaux"
Set-Location ..
# TODO: Remove this when merging
# Run the install script
if ( -not (Test-Path "$SetupPath")) {
Throw "Repository contains no subdirectory '$SetupPath'."
}
Set-Location $SetupPath
# Run upgrade-windows to install the rest of the dependency chain.
$upgradeArgs = if ($AllowAdministrator) {" -AllowAdministrator"} else {""}
$upgradeArgs += if ($DryRun) {" -DryRun"} else {""}
PowerShell ("./upgrade-windows.ps1 -Install -NoPrompt" + $upgradeArgs)
Reset-Path
# Open VSCode in the repository location
Write-Host "`nOpening Visual Studio Code"
Set-Location $RepoPath
Invoke-DryRun "code ."
Set-Location $dir
if ( -not $NoPrompt ) {
Write-Host "`nPress any key to exit."; [void][Console]::ReadKey(1)
}

View File

@ -0,0 +1,214 @@
<#
.synopsis
Dependency updater for Mind Reader on Windows.
This script expects to be run from Mind_Reader/setup-development
.description
Updates dependencies (NodeJS, Python, etc.), VSCode, NVDA
The script uses winget (A.K.A. "App Installer") to download and install the latest versions of each dependency, defined in winget/dependencies.json
Winget comes preinstalled on Windows 11 (21H2)/10 (21H1) or newer, and can be installed on Windows 10 1704+ through the Windows Store.
If you download Microsoft's developer VM, you have it!
As WinGet is built into Windows, it sidesteps any annoying third-party package managers, and is the lowest common denominator for package installation.
.link
https://github.com/We-Dont-Byte/Mind_Reader/
.parameter GitDir
Path to clone the git repo into (Default: $HOME/git/)
.parameter AllowAdministrator
Force-allow running this script as Administrator (not recommended, despite the frequent UAC prompts!)
.parameter NoPrompt
Disable all prompts for user input, and all waiting. (not recommended when combined with AllowAdministrator!)
.parameter Install
Force installation/upgrade of all modules, even if already present on the system.
.parameter DryRun
Perform a "dry run" of the script, changing directories and running commands, but without modifying anything.
.example
./upgrade-windows.ps1
Perform a default upgrade of all Mind Reader dependencies
.example
./upgrade-windows.ps1 -DryRun
Perform a dry run of the upgrade process, so you can evaluate what commands will be run
.example
./upgrade-windows.ps1 -NoPrompt
Don't prompt for user input when upgrading
.example
./upgrade-windows.ps1 -AllowAdministrator
Allow script to be run as Administrator
#>
param (
[switch]$AllowAdministrator, # Force allow installation as administrator
[switch]$NoPrompt, # Disable the 3-second wait and press-any-key prompt
[switch]$Install, # Perform all installations, even when commands are present
[switch]$DryRun, # Run script without installing
[switch]$NoWinget # Don't update dependdencies with winget
)
# .description
# Get-CommandAvailable: Checks whether a given command is available.
# If command is available, returns $false
function Get-CommandAvailable {
param ($command)
# Use a wildcard here so the command doesn't throw an exception we'd have to trycatch
# It's not a filthy hack if it's elegant!
RETURN (Get-Command -Name $command*)
}
#.description
# Invoke-Dryrun a powershell statement
function Invoke-Dryrun {
param ([string] $command)
$prompt = "> "
if ($DryRun) {
Write-Host "$prompt$command [dry]" -ForegroundColor darkgray
}
else {
Write-Host "$prompt$command" -ForegroundColor white
Invoke-Expression $command
}
}
#.description
# Reset-Path: Reload the Path environment variable
function Reset-Path {
Write-Output "Reloading Path..."
#* This code was created by user [mpen](https://stackoverflow.com/users/65387/mpen) on [StackOverflow](https://stackoverflow.com/a/31845512) and is used in accordance with Creative Commons CC BY-SA 3.0
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
}
# Check if Winget is available
if ( -not (Get-CommandAvailable winget) ) {
Write-Warning "It looks like winget isn't available.`n"
Write-Host "Update 'App Installer' through the Microsoft Store, or grab the '.msixbundle' from the winget-cli repository:"
Write-Host "( https://github.com/microsoft/winget-cli/releases/latest )`n" -ForegroundColor White
exit
}
# Check if the user ran the script with administrator privileges.
# Warn them.
if ( ([Security.Principal.WindowsIdentity]::GetCurrent().Groups -contains 'S-1-5-32-544') ) {
# If an administrator requests installation as administator,
# for example, to keep UAC prompts to a minimum, allow it.
if ($AllowAdministrator) {
# If you pass -AllowAdministrator -NoPrompt as an arg, you're very aware of the damage this script could do to your build env, and you just don't care
# The true chad of sysadmins.
if (!$NoPrompt) {
Write-Warning "Script was run as Administrator. Exit now if you didn't mean to do this!"
for ( $i = 3; $i -gt 0; $i--) {
Write-Host "Press Ctrl+C to exit. Continuing in $i...`r" -NoNewLine
Start-Sleep 1
}
Write-Host "Press any key to continue... "
[void][Console]::ReadKey(1) # Equivalent to Command Prompt's `pause` command
}
}
else {
# Throw a fatal errorOccurred if the user tries to run as administrator.
Throw "Script must be run as a normal user."
}
}
# Import the packages from dependencies.json (autogenerated file, do not edit!)
if ( -not $NoWinget) {
Write-Host "`nInstalling packages with winget..."
Invoke-Dryrun 'winget install Microsoft.VisualStudio.2022.BuildTools --override "--wait --quiet --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"'
Invoke-Dryrun "winget import winget/dependencies.json"
# Reload the PATH, so we can use some of those sweet new commands we just installed
Reset-Path
}
# Check whether everything is available now:
$errorOccurred = 0
if ( -not (Get-CommandAvailable code) ) {
$errorOccurred += 1; Write-Host -ForegroundColor red "Visual Studio Code not available"
}
if ( -not (Get-CommandAvailable node) ) {
$errorOccurred += 2; Write-Host -ForegroundColor red "NodeJS not available"
}
if ( -not (Get-CommandAvailable npm ) ) {
$errorOccurred += 4; Write-Host -ForegroundColor red "Node Package Manager not available";
}
if ( $errorOccurred ) { exit }
# .description
# EnsureNodePackageInstalled:
# Checks for the presence of a cmdlet with a given name
# If it's not found, attempt to install it using npm
# If it's still not found, abort (this may not be good behavior?)
function EnsureNodePackageInstalled {
param (
[string[]]$command
)
if ( ($Install) -or -not (Get-CommandAvailable $command[0]) ) {
Write-Host "`nInstalling $($command[0])..."
Invoke-Dryrun "npm install -g $([string]$command)"
Reset-Path
if ( -not (Get-CommandAvailable $command[0])) {
Throw "$command failed to install. Aborting."
}
}
else {
Write-Host "`n$($command[0]) already installed." -ForegroundColor green
}
}
# Check if electron-rebuild is installed, if not, install it
EnsureNodePackageInstalled electron-rebuild
# These are useful (but not necessary) packages to have installed when working on new VSCode extensions
EnsureNodePackageInstalled yo, generator-code
# We're about to do some path traversal, so save the current directory
$prev_directory = $pwd
# install NodeJS dependencies for this extension
Write-Host "`nInstalling NodeJS Dependencies..."
Set-Location ..\..
Invoke-Dryrun "npm install"
# Run npm audit fix to upgrade vulnerable dependencies, except breaking changes.
Invoke-Dryrun "npm audit fix"
# if we're on a known VSCode version, go ahead and run electron-rebuild
switch -Regex (code --version) {
#?: Do we update this in the future, or stop maintaining it and remove this entire switch block?
"1\.67\.\d+" { $electronversion = "17.4.1"; break } # April 2022 update
"1\.66\.\d+" { $electronversion = "17.2.0"; break } # March 2022 update
default { $electronversion = $false } # Unknown update
}
if ( $electronversion ) {
Write-Host "`nRebuilding Electron for your version of VSCode..."
Invoke-Dryrun "electron-rebuild --version='$electronversion'"
Write-Host "Done!" -ForegroundColor green
}
else {
Write-Host "`nOpen Visual Studio Code, select the `"Help`" tab in the Toolbar, and go to `"About`".`nYou should see a page that looks like the following:" -ForegroundColor darkcyan
Write-Host " `(i`) Visual Studio Code`n`n Version: 1.66.2 `(user setup`)`n Commit: [Commit ID]`n Date: 2022-04-11T07:46:01.075Z`n Electron: 17.2.0`n [ ... ]" -ForegroundColor White
Write-Host "Note the Electron version `(17.2.0 in the above example`)." -ForegroundColor darkcyan
Write-Host "Run the command " -NoNewLine
Write-Host "electron-rebuild --version ELECTRON_VERSION" -NoNewLine -ForegroundColor green
Write-Host " in Mind Reader`'s root folder.`n"
}
# Return from whence we came
Set-Location $prev_directory
if ( -not $NoPrompt ) {
Write-Host "`nPress any key to exit."; [void][Console]::ReadKey(1)
}

View File

@ -0,0 +1,29 @@
{
"$schema": "https://aka.ms/winget-packages.schema.2.0.json",
"CreationDate": "2022-04-23T21:13:03.702-00:00",
"Sources": [
{
"Packages": [
{
"PackageIdentifier": "Python.Python.3"
},
{
"PackageIdentifier": "Git.Git"
},
{
"PackageIdentifier": "OpenJS.NodeJS.LTS"
},
{
"PackageIdentifier": "Microsoft.VisualStudioCode"
}
],
"SourceDetails": {
"Argument": "https://winget.azureedge.net/cache",
"Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe",
"Name": "winget",
"Type": "Microsoft.PreIndexed.Package"
}
}
],
"WinGetVersion": "1.2.10271"
}

View File

@ -1,6 +1,7 @@
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as path from 'path'; import * as path from 'path';
import HubManager from '../hubManager'; import HubManager from '../hubManager';
//import EV3Manager from '../ev3Manager';
import { CommandEntry } from './commandEntry'; import { CommandEntry } from './commandEntry';
@ -33,12 +34,23 @@ export const hubCommands: CommandEntry[] = [
{ {
name: 'mind-reader.deleteProgram', name: 'mind-reader.deleteProgram',
callback: deleteProgram callback: deleteProgram
}, }/*,
{
name: 'mind-reader.ev3.test',
callback: ev3test
}*/
]; ];
// Current connected hub // Current connected hub
let hub: HubManager | null = null; let hub: HubManager | null = null;
/*
let ev3: EV3Manager | null = null;
async function ev3test(): Promise<void> {
ev3 = await EV3Manager.activate();
ev3.test();
}
*/
async function connectHub(): Promise<void> { async function connectHub(): Promise<void> {
if (hub && hub.isOpen()) { if (hub && hub.isOpen()) {
vscode.window.showWarningMessage('LEGO Hub is already connected, reconnecting...'); vscode.window.showWarningMessage('LEGO Hub is already connected, reconnecting...');
@ -53,7 +65,7 @@ async function connectHub(): Promise<void> {
return; return;
} }
let portPath: string | undefined = vscode.workspace.getConfiguration('mindReader.connection').get('portPath'); let portPath: string | undefined = vscode.workspace.getConfiguration('mind-reader.connection').get('portPath');
if (!portPath) { if (!portPath) {
let slots: vscode.QuickPickItem[] = []; let slots: vscode.QuickPickItem[] = [];

View File

@ -1,24 +1,19 @@
import * as vscode from 'vscode'; import * as vscode from "vscode";
import * as fs from 'fs';
import { CommandEntry } from './commandEntry'; import { CommandEntry } from "./commandEntry";
export const navCommands: CommandEntry[] = [ export const navCommands: CommandEntry[] = [
{ {
name: 'mind-reader.openWebview', name: 'mind-reader.openWebview',
callback: openWebview, callback: openWebview,
}, },
{ {
name: 'mind-reader.openKeyBindWin', name: "mind-reader.openKeybinds",
callback: () => openKeyBindWin('Windows') callback: () => vscode.commands.executeCommand("workbench.action.openGlobalKeybindings", "mind-reader"),
},
{
name: 'mind-reader.openKeyBindMac',
callback: () => openKeyBindWin('Mac'),
}, },
//Navigation Keys...... //Navigation Keys......
// TODO: Why is this here? Extensions can rebind existing keybinds.
{ {
name: 'mind-reader.showAllSymbols', name: 'mind-reader.showAllSymbols',
callback: () => vscode.commands.executeCommand('workbench.action.showAllSymbols'), callback: () => vscode.commands.executeCommand('workbench.action.showAllSymbols'),
@ -82,35 +77,58 @@ export const navCommands: CommandEntry[] = [
// COMMAND CALLBACK IMPLEMENTATIONS // COMMAND CALLBACK IMPLEMENTATIONS
function openWebview(): void { function openWebview(): void {
//vscode.commands.executeCommand('workbench.action.zoomOut');
const panel = vscode.window.createWebviewPanel( const panel = vscode.window.createWebviewPanel(
'mindReader', // Identifies the type of the webview. Used internally "mind-reader", // Identifies the type of the webview. Used internally
'Mind Reader', // Title of the panel displayed to the user "Mind Reader", // Title of the panel displayed to the user
vscode.ViewColumn.One, // Editor column to show the new webview panel in. vscode.ViewColumn.One, // Editor column to show the new webview panel in.
{} {}
); // Webview options. More on these later. ); // Webview options. More on these later.
panel.webview.html = getWebviewContent('media/html/main.html'); panel.webview.html = getWebviewContent();
} }
function getWebviewContent(filepath: string) { function getWebviewContent() {
return fs.readFileSync(filepath, {encoding: 'utf-8'}); return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mind Reader</title>
</head>
<body>
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6a4XaqHkKcxJ6ZFms1RNrRurcOfl-diW90DAdpAx0Kv-rtrLJXovIhcUpayqFHATkrQ&usqp=CAU" width="600" />
<p></p>
<h1>Welcome to Mind Reader!</h1>
<p>We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.</p>
<ul>
<li>
This tool includes features such as a hotkey that says how many spaces in the text starts, an Accessibility Pane,
Audio Alerts, and an advanced settings window.
<br>
The tool has hotkeys for both PC and Mac commands.
</li>
<li>This system is intended for everyone, but primarily for students K-12 who are visually impaired. </li>
<li>
Our goal is to provide an enhanced experience for students who are visually impaired that is transparent to
sighted students.
<br>
This allows for everyone to use the same software solution, whether or not they are
vision impaired.
</li>
</ul>
<p>Use the following key binding to bring up a page for all key bindings for windows
<br>
Control and Shift and 8
</p>
<p>Use this key binding to do the same for mac computers:
<br>
Command and Shift and 9
</p>
<h2>This is the Lego Spike Prime!</h2z>
<p></p>
<img src="https://cdn.vox-cdn.com/thumbor/qoaa6N2ppl7oj97MR-aj43qPy0w=/0x0:1024x576/920x613/filters:focal(431x207:593x369):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/63339099/lego_spike.0.png" width="300" />
<p></p>
<a href="https://www.lego.com/en-us/product/lego-education-spike-prime-set-45678">Get the robot!</a>
</body>
</html>`;
} }
function openKeyBindWin(os: 'Mac' | 'Windows'): void {
//vscode.commands.executeCommand('workbench.action.zoomOut');
const panel = vscode.window.createWebviewPanel(
'mindReader', // Identifies the type of the webview. Used internally
'MR Key Bindings', // Title of the panel displayed to the user
vscode.ViewColumn.One, // Editor column to show the new webview panel in.
{}
); // Webview options. More on these later.
if (os === 'Windows') {
panel.webview.html = getWebviewContent('media/html/winkeys.html');
} else if (os === 'Mac') {
panel.webview.html = getWebviewContent('media/html/mackeys.html');
}
}

View File

@ -1,74 +1,275 @@
import * as vscode from 'vscode'; "use strict";
import * as pl from '../pylex'; import pl = require("../pylex");
import { CommandEntry } from './commandEntry'; import { CommandEntry } from './commandEntry';
import { Position, Selection, TextEditor, TextLine, window, workspace } from "vscode";
export const textCommands: CommandEntry[] = [ export const textCommands: CommandEntry[] = [
{
name: 'mind-reader.getLineNumber',
callback: getLineNumber,
},
{ {
name: 'mind-reader.getIndent', name: 'mind-reader.getIndent',
callback: getIndent, callback: getIndent,
}, },
{ {
name: 'mind-reader.runLineContext', name: 'mind-reader.getLeadingSpaces',
callback: getLeadingSpaces,
},
{
name: 'mind-reader.selectLeadingWhitespace',
callback: selectLeadingWhitespace
},
{
name: 'mind-reader.getNumberOfSelectedLines',
callback: getNumberOfSelectedLines,
},
{
name: 'mind-reader.getLineScope',
callback: runLineContext, callback: runLineContext,
}, },
{ {
name: 'mind-reader.runCursorContext', name: 'mind-reader.getWordsUnderCursor',
callback: runCursorContext callback: runCursorContext
} }
] ];
function getIndent(): void { /** Helper Function
let editor = vscode.window.activeTextEditor; *
if(editor) * @param editor
{ * @returns numSpaces
let lineNum = editor.selection.active.line + 1; ** There are two methods that can be used to find the leading spaces:
let textLine = editor.document.lineAt(lineNum - 1); ** method 1:
if(textLine.isEmptyOrWhitespace) ** calculates the number of leading spaces by finding the length of the current line
{ ** then subtracting from that the length of the text after trimming the whitespace at the start
vscode.window.showInformationMessage("Line number " + lineNum.toString() + " Is Empty"); ** which will equal the number of whitespace characters
**
** TO-USE: set calculateLeadingSpaces to true
**
** method 2 (default):
** finds the index position of the first non-whitespace character in a 0-index
** this number will equal the number of spaces preceding the non-whitespace character
** due to the nature of 0-indexes.
**
** TO-USE: set calculateLeadingSpaces to false
*/
function fetchNumberOfLeadingSpaces(editor: TextEditor | undefined): number {
let numSpaces: number = 0;
if (editor) {
/*
* set true to use method 1: find the number of leading spaces through arithmetic
* set false to use method 2: find the index position of the first non-whitespace character in a 0-index
* default: false
*/
const calculateLeadingSpaces: boolean = false; // change boolean value to change method
const line : TextLine = fetchLine(editor);
/* If true, calculate by arithmetic otherwise get index */
numSpaces = (calculateLeadingSpaces)
? pl.Lexer.getLeadingSpacesByArithmetic(line)
: pl.Lexer.getLeadingSpacesByIndex(line);
} }
else
{ return numSpaces;
}
/** Helper Function
* * This function returns the number of selected lines in the active text editor window
@param editor
@returns numberOfSelectedLines
*/
function fetchNumberOfSelectedLines(editor: TextEditor | undefined): number {
let numberOfSelectedLines: number = 0;
if (editor) {
numberOfSelectedLines = editor.selections.reduce((prev, curr) => prev + (curr.end.line - curr.start.line), 1);
}
return numberOfSelectedLines;
}
/** Helper Function
** This function returns the line number of the active text editor window
* @param editor
* @returns editor!.selection.active.line + 1
*/
function fetchLineNumber(editor: TextEditor | undefined): number {
return editor!.selection.active.line + 1; // line numbers start at 1, not 0, so we add 1 to the result
}
/** Helper Function
** This function returns the text from the current line of the active text editor window
* @param editor
* @returns editor.document.lineAt(fetchLineNumber(editor) - 1)
*/
function fetchLine(editor: TextEditor | undefined): TextLine {
return editor!.document.lineAt(fetchLineNumber(editor) - 1); // We want the line index, so we remove the 1 we added to the result in fetchLineNumber
}
/* Function
* Function to return the number of selected (highlighted) lines
* Changes output to 'Line' for 1 line and 'Lines' for all other instances
*/
function getNumberOfSelectedLines(): void {
const editor: TextEditor | undefined = window.activeTextEditor;
if (editor) {
const numberOfSelectedLines: number = fetchNumberOfSelectedLines(editor);
(numberOfSelectedLines !== 1)
? window.showInformationMessage(`${numberOfSelectedLines.toString()} Lines Selected`)
: window.showInformationMessage(`${numberOfSelectedLines.toString()} Line Selected`);
}
else {
window.showErrorMessage('No document currently active');
}
}
/* Function
* Outputs the current line number the cursor is on
*/
function getLineNumber(): void {
const editor: TextEditor | undefined = window.activeTextEditor;
if (editor) {
const lineNum: number = fetchLineNumber(editor);
window.showInformationMessage(`Line ${lineNum.toString()}`);
}
else {
window.showErrorMessage('No document currently active');
}
}
/* Function
* Used to get the number of indents on a line
*/
function getIndent(): void {
const editor: TextEditor | undefined = window.activeTextEditor;
if (editor) {
const lineNum: number = (fetchLineNumber(editor));
const line : TextLine = fetchLine(editor);
if (line.isEmptyOrWhitespace) {
window.showInformationMessage(`Line ${lineNum.toString()} is Empty`);
}
else {
// Grab tab format from open document // Grab tab format from open document
let tabFmt = { const tabFmt: pl.TabInfo = {
size: editor.options.tabSize as number, size: typeof editor.options.tabSize === 'number'? editor.options.tabSize: 4,
hard: !editor.options.insertSpaces hard: !editor.options.insertSpaces
}; };
let i = pl.Lexer.getIndent(textLine.text, tabFmt); const i: number = pl.Lexer.getIndent(line.text, tabFmt);
vscode.window.showInformationMessage("Line Number " + lineNum.toString() + " Indentation " + i.toString());
(i !== 1)
? window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indents`)
: window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indent`);
} }
} }
else{ else {
vscode.window.showErrorMessage('No document currently active'); window.showErrorMessage('No document currently active');
}
}
/* Function
* Returns the number of leading spaces on the line the cursor is on
*/
function getLeadingSpaces(): void {
const editor: TextEditor | undefined = window.activeTextEditor;
if (editor) {
const lineNum : number = fetchLineNumber(editor);
const line : TextLine | undefined = fetchLine(editor);
if (line.isEmptyOrWhitespace) {
window.showInformationMessage(`Line ${lineNum.toString()} is empty`);
}
else {
const numSpaces = fetchNumberOfLeadingSpaces(editor);
/* Ternary operator to change the tense of 'space' to 'spaces' for the output if numSpaces is 0 or greater than 1 */
(numSpaces !== 1)
? window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} spaces`)
: window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} space`);
}
}
else {
window.showErrorMessage('No document currently active');
}
}
/* Function
* Selects the leading whitespace at the beginning of a line
* This feature was a request from Senior Design Day Spring 2022
*/
function selectLeadingWhitespace(): void {
const editor : TextEditor | undefined = window.activeTextEditor;
if (editor) {
const numSpaces = fetchNumberOfLeadingSpaces(editor); // This will be used for the output message
const lineNum : number = (fetchLineNumber(editor)); // Get the displayed line number
/* If numSpaces isn't greater than 1, then there is no leading whitespace to select */
if (numSpaces >= 1) {
const line : TextLine = fetchLine(editor);
const startPos: number = line.range.start.character; // Start at the starting character position
const endPos : number = line.firstNonWhitespaceCharacterIndex; // End at the first non whitespace character index
/* Apply our selection */
/* We need to subtract 1 from lineNum because we added 1 during the fetchLineNumber above and we want the 0-index for position, so remove it */
editor.selection = new Selection(new Position((lineNum - 1), startPos), new Position((lineNum - 1), endPos));
/* Ternary operator to change the tense of 'space' to 'spaces' for the output if numSpaces is 0 or greater than 1 */
(numSpaces !== 1)
? window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} spaces selected`)
: window.showInformationMessage(`Line ${lineNum.toString()}: ${numSpaces.toString()} space selected`);
// Move the cursor to the new selection
window.showTextDocument(editor.document);
}
else {
window.showErrorMessage(`Line ${lineNum.toString()}: No leading spaces to select!`); // No whitespace to select
}
}
else {
window.showErrorMessage('No document currently active'); // No active document
} }
} }
function runLineContext(): void { function runLineContext(): void {
let editor = vscode.window.activeTextEditor; const editor: TextEditor | undefined = window.activeTextEditor;
if (editor){
if (editor) {
// current text and line number // current text and line number
let editorText = editor.document.getText(); const editorText: string = editor.document.getText();
let line = editor.selection.active.line; const line : number = editor.selection.active.line;
// get tab info settings // get tab info settings
let size = parseInt(editor.options.tabSize as string); const size : number = typeof editor.options.tabSize === 'number'? editor.options.tabSize: 4;
let hard = !editor.options.insertSpaces; const hard : boolean = !editor.options.insertSpaces;
// initialize parser // initialize parser
let parser = new pl.Parser(editorText, {size, hard}); const parser : pl.Parser = new pl.Parser(editorText, {
size,
hard
});
parser.parse(); parser.parse();
const context: pl.LexNode[] = parser.context(line);
let context = parser.context(line);
// build text // build text
let contentString = createContextString(context, line); const contentString: string = createContextString(context, line);
vscode.window.showInformationMessage(contentString);
} else { window.showInformationMessage(contentString);
vscode.window.showErrorMessage('No document currently active'); }
else {
window.showErrorMessage('No document currently active');
} }
} }
@ -77,92 +278,111 @@ function createContextString(context: pl.LexNode[], line: number): string {
throw new Error('Cannot create context string for empty context'); throw new Error('Cannot create context string for empty context');
} }
let contextString = 'Line ' + (line+1); // 1 based let contextString: string = `Line ${line + 1}`; // 1 based
// Print the current line
if (context[0].token && context[0].token.attr) { if (context[0].token && context[0].token.attr) {
contextString += ': ' + context[0].token.type.toString() + ' ' + context[0].token.attr.toString(); let tokenTypeString: string = `${context[0].token.type.toString()}`;
} contextString += `: ${tokenTypeString !== pl.PylexSymbol.STATEMENT?tokenTypeString:""
for (let i = 1; i < context.length; i++) { } ${context[0].token.attr.toString()}`;
let node = context[i];
if (node.label === 'root') {
// root
contextString += ' in the Document Root';
continue;
} }
if (node.token!.type !== pl.PylexSymbol.EMPTY && for (let i: number = 1; i < context.length; i++) {
node.token!.type !== pl.PylexSymbol.INDENT) { const node: pl.LexNode = context[i];
contextString += ' inside ' + node.token!.type.toString(); const inside: string = "inside";
if (node.token!.attr) { // Node contains information relevant to the current line
contextString += ' ' + node.token!.attr.toString(); if (node.token && node.token.type !== pl.PylexSymbol.EMPTY &&
node.token.type !== pl.PylexSymbol.STATEMENT) {
contextString += ` ${inside} ${node.token.type.toString()}`;
if (node.token.attr) {
contextString += ` ${node.token.attr.toString()}`;
} }
} }
// Node is the document root
if (node.label === 'root') {
// Append the name (relative path) of the document in the workspace
if (window.activeTextEditor?.document.uri) {
contextString += ` ${inside} ${workspace.asRelativePath(window.activeTextEditor?.document.uri)}`;
} else {
contextString += ` ${inside} the Document`;
} }
continue;
}
}
return contextString; return contextString;
} }
// find up to `n` words around the cursor, where `n` is /*
// the value of `#mindReader.reader.contextWindow` * find up to `n` words around the cursor, where `n` is
* the value of `#mind-reader.reader.contextWindow`
*/
function runCursorContext(): void { function runCursorContext(): void {
let editor = vscode.window.activeTextEditor; const editor: TextEditor | undefined = window.activeTextEditor;
if (!editor) { if (!editor) {
vscode.window.showErrorMessage('RunCursorContext: No Active Editor'); window.showErrorMessage('RunCursorContext: No Active Editor');
return; return;
} }
const cursorPos: vscode.Position = editor.selection.active; const cursorPos : Position = editor.selection.active;
const text: string = editor.document.lineAt(cursorPos).text; const text : string = editor.document.lineAt(cursorPos).text;
const windowSize: number = vscode.workspace.getConfiguration('mindReader').get('reader.contextWindow')!; const windowSize : any = workspace.getConfiguration('mind-reader').get('reader.contextWindow');
let trimmedText: string = text.trimStart(); // trim leading whitespace
let trimmedText = text.trimStart(); // trim leading whitespace const leadingWS : number = text.length - trimmedText.length; // # of characters of leading whitespace
let leadingWS = text.length - trimmedText.length; // # of characters of leading whitespace let pos : number = leadingWS;
trimmedText = trimmedText.trimEnd(); // trim trailing whitespace const maxPos : number = text.length;
let pos = leadingWS;
let maxPos = text.length;
// clamp cursor start/end to new range // clamp cursor start/end to new range
let col = cursorPos.character; // effective column of the cursor position let col : number = cursorPos.character; // effective column of the cursor position
trimmedText = trimmedText.trimEnd(); // trim trailing whitespace
if (col < leadingWS) { if (col < leadingWS) {
// move effective start to first non-whitespace character in the line // move effective start to first non-whitespace character in the line
col = leadingWS; col = leadingWS;
} else if (col > leadingWS + trimmedText.length - 1) { }
else if (col > leadingWS + trimmedText.length - 1) {
// move effective end to last non-whitespace character in the line // move effective end to last non-whitespace character in the line
col = leadingWS + trimmedText.length - 1; col = leadingWS + trimmedText.length - 1;
} }
// generate list of space separate words with range data (start, end) // generate list of space separate words with range data (start, end)
// TODO: can find user position to be done in one pass // TODO: can find user position to be done in one pass
let spaceWords: {word: string, start: number, end: number}[] = []; const spaceWords: any[] = [];
while (pos < maxPos && trimmedText.length > 0) { while (pos < maxPos && trimmedText.length > 0) {
let word = trimmedText.replace(/ .*/, ''); const word: string = trimmedText.replace(/ .*/, '');
spaceWords.push({word, start: pos, end: pos+word.length});
spaceWords.push({
word,
start: pos,
end: pos + word.length
});
// remove processed word from trimmed text // remove processed word from trimmed text
const oldText = trimmedText; const oldText: string = trimmedText;
trimmedText = trimmedText.replace(/[^ ]+/, '').trimStart(); trimmedText = trimmedText.replace(/[^ ]+/, '').trimStart();
// update pos to start of next word // update pos to start of next word
pos += oldText.length - trimmedText.length; pos += oldText.length - trimmedText.length;
} }
// find word the user is in // find word the user is in
let contextStart: number = -1, contextEnd: number = -1; let contextStart: number = -1;
for (let i = 0; i < spaceWords.length; i++) { let contextEnd : number = -1;
for (let i: number = 0; i < spaceWords.length; i++) {
if (col >= spaceWords[i].start && col <= spaceWords[i].end) { if (col >= spaceWords[i].start && col <= spaceWords[i].end) {
// found the word // found the word
contextStart = Math.max(0, i - windowSize); // clamp start index contextStart = Math.max(0, i - windowSize); // clamp start index
contextEnd = Math.min(spaceWords.length, i + windowSize + 1); // clamp end index contextEnd = Math.min(spaceWords.length, i + windowSize + 1); // clamp end index
// construct cursor context string // construct cursor context string
let contextString = ''; let contextString: string = '';
for (let i = contextStart; i < contextEnd; i++) {
for (let i: number = contextStart; i < contextEnd; i++) {
contextString += spaceWords[i].word + ' '; contextString += spaceWords[i].word + ' ';
} }
// output cursor context string // output cursor context string
vscode.window.showInformationMessage(contextString); window.showInformationMessage(contextString);
return; return;
} }
} }
} }

31
src/ev3Manager.ts Normal file
View File

@ -0,0 +1,31 @@
import * as vscode from 'vscode';
//import * as fs from 'fs';
//import { logger } from './extension';
export default class EV3Manager {
private ev3devBrowser: vscode.Extension<any> | undefined = vscode.extensions.getExtension("ev3dev.ev3dev-browser");
private constructor() {}
public test() {
//console.log(this.ev3devBrowser);
// This seems to be the only thing we, as an extension,
// are allowed to do with this other extension.
vscode.commands.executeCommand("ev3devBrowser.action.pickDevice", null);
}
public static activate(): Promise<EV3Manager> {
return new Promise (async (resolve) => {
try {
let mgr = new EV3Manager();
// Wait for ev3devBrowser to start
await mgr.ev3devBrowser?.activate();
// Return ev3Manager
return resolve(mgr);
}
catch (err) {
throw err;
}
});
}
}

View File

@ -1,49 +1,47 @@
import * as vscode from 'vscode'; import * as vscode from "vscode";
import * as pl from './pylex'; import * as pl from "./pylex";
import CommandNodeProvider from "./commandNodeProvider";
import Logger from "./log";
import { lineHighlighter } from "./lineHighlighter";
import { import { accessCommands, hubCommands, navCommands, textCommands } from "./commands";
accessCommands,
hubCommands,
navCommands,
textCommands
} from './commands';
import CommandNodeProvider from './commandNodeProvider';
import Logger from './log';
// Output Logger // Output Logger
const product: string = vscode.workspace.getConfiguration('mindReader').get('productType')!; const product: string = vscode.workspace.getConfiguration("mind-reader").get("productType")!;
const outputChannel = vscode.window.createOutputChannel(product + " Output"); const outputChannel = vscode.window.createOutputChannel(product + " Output");
export const logger = new Logger(outputChannel); export const logger = new Logger(outputChannel);
let parser: pl.Parser = new pl.Parser(); let parser: pl.Parser = new pl.Parser();
export function activate(context: vscode.ExtensionContext) { export function activate(context: vscode.ExtensionContext) {
vscode.window.showInformationMessage('Mind_Reader is loaded!'); // Engage LineHighlighter
lineHighlighter();
parser.parse('Beep Boop'); parser.parse("Beep Boop");
const allCommands = [ const allCommands = [
accessCommands, accessCommands,
hubCommands, hubCommands,
navCommands, navCommands,
textCommands textCommands,
].flat(1); ].flat(1);
// Register Commands // Register Commands
allCommands.forEach(command => { allCommands.forEach((command) => {
let disposable = vscode.commands.registerCommand( context.subscriptions.push(
command.name, vscode.commands.registerCommand(command.name, command.callback)
command.callback
); );
context.subscriptions.push(disposable);
}); });
let accessProvider = new CommandNodeProvider([accessCommands, textCommands].flat(1)); let accessProvider = new CommandNodeProvider(
vscode.window.registerTreeDataProvider('accessActions', accessProvider); [accessCommands, textCommands].flat(1)
);
vscode.window.registerTreeDataProvider("accessActions", accessProvider);
let hubProvider = new CommandNodeProvider(hubCommands); let hubProvider = new CommandNodeProvider(hubCommands);
vscode.window.registerTreeDataProvider('hubActions', hubProvider); vscode.window.registerTreeDataProvider("hubActions", hubProvider);
vscode.window.showInformationMessage("Mind Reader finished loading!");
} }
export function deactivate() {} export function deactivate() {}

View File

@ -76,12 +76,14 @@ export default class HubManager {
// get full lines in buffer // get full lines in buffer
let msgs = this.receiveBuffer.split(/\r/); // split by newline let msgs = this.receiveBuffer.split(/[\r>]/); // split by newline
this.receiveBuffer = msgs.pop()!; // store unhandled data this.receiveBuffer = msgs.pop()!; // store unhandled data
msgs = msgs.filter(x => !x.startsWith('{"m":0,"p":')); // drop sensor broadcast response spam msgs = msgs.filter(x => !x.match(/{"m":\d+,"p":/)); // drop sensor broadcast response spam
for (const msg of msgs) { for (const msg of msgs) {
// check if message is actually json
if (!msg.includes("{")) { continue; }
// check if this msg is a response to a pending request // check if this msg is a response to a pending request
try { try {
let json: { [key: string]: any }; let json: { [key: string]: any };
@ -112,11 +114,15 @@ export default class HubManager {
case 'runtime_error': case 'runtime_error':
logger.error(Buffer.from(params[3], 'base64').toString()); logger.error(Buffer.from(params[3], 'base64').toString());
break; break;
case 2:
logger.info(`Battery at ${params[0]}V`);
break;
} }
vscode.window.showErrorMessage("Program Error."); vscode.window.showErrorMessage("Program Error.");
console.log(`Program error: ${msg}`);
} }
} catch (err) { } catch (err) {
console.log('Could not parse JSON:', msg); console.log('Could not parse JSON:', msg, err);
} }
} }
} }

311
src/lineHighlighter.ts Normal file
View File

@ -0,0 +1,311 @@
/**
* ?
* ?
* ?
* ?
* ?
* ?
* ! Initial Setup:
* ! Open settings.json (ctrl+shift+p type 'settings' choose: 'Preferences: Open Settings (JSON))
* ! Add the following to the bottom (may have to add a comma to the line above if it's not there, also remove the *s):
* "mind-reader.lineHighlighter.isEnabled" : true,
* "mind-reader.lineHighlighter.multiLineIsEnabled" : false,
*
* "mind-reader.lineHighlighter.backgroundColor" : "#232C5C",
* "mind-reader.lineHighlighter.outlineColor" : "#4866FE",
* "mind-reader.lineHighlighter.outlineWidth" : "1px",
* "mind-reader.lineHighlighter.outlineStyle" : "solid",
*
* "mind-reader.lineHighlighter.borderColorTop" : "#FFFFFF",
* "mind-reader.lineHighlighter.borderColorRight" : "#FFFFFF",
* "mind-reader.lineHighlighter.borderColorBottom" : "#FFFFFF",
* "mind-reader.lineHighlighter.borderColorLeft" : "#FFFFFF",
*
* "mind-reader.lineHighlighter.borderWidthTop" : "1px",
* "mind-reader.lineHighlighter.borderWidthRight" : "16px",
* "mind-reader.lineHighlighter.borderWidthBottom" : "1px",
* "mind-reader.lineHighlighter.borderWidthLeft" : "1px",
*
* "mind-reader.lineHighlighter.borderStyleTop" : "solid",
* "mind-reader.lineHighlighter.borderStyleRight" : "solid",
* "mind-reader.lineHighlighter.borderStyleBottom" : "solid",
* "mind-reader.lineHighlighter.borderStyleLeft" : "solid",
*
* "mind-reader.lineHighlighter.fontStyle" : "normal",
* "mind-reader.lineHighlighter.fontWeight" : "bolder",
* "mind-reader.lineHighlighter.textColor" : "#FFFFFF",
*
* ! Restart VSCode for changes to take effect (if they didn't automatically)
* ! Afterwards you can now edit using the settings window, or manually edit them
* ! directly in settings.json by editing the values.
*
* TODO: FEATURE: Add ability for user to change options through a command pallette configurator
* TODO: FEATURE: Add hotkey to toggle linehighlighter on/off
* TODO: BUG: Adding the settings configurator made default settings break (if no values are found in settings.json)
**/
'use strict';
import { Position, window, workspace, TextEditorDecorationType, TextEditor, WorkspaceConfiguration, Range } from 'vscode';
export { lineHighlighter };
let highlightStyle: TextEditorDecorationType;
function lineHighlighter(): void {
let highlightStyle : TextEditorDecorationType = getHighlighterStyle();
let activeTextEditor : TextEditor | undefined = window.activeTextEditor;
let isEnabled : boolean | undefined = getHighlighterStatus();
let multiLineIsEnabled: boolean | undefined = getMultiLineHighlighterStatus();
/**
* Trigger the line highlight when the extension loads so current line gets highlighted
*/
triggerHighlight();
/**
* Trigger for when the active text editor changes
*/
window.onDidChangeActiveTextEditor((editor) => {
if (!editor) {
return;
}
triggerHighlight();
});
/**
* Trigger for when text selection changes
*/
window.onDidChangeTextEditorSelection((editor) => {
if (!editor.textEditor) {
return;
}
triggerHighlight();
});
/**
* Trigger for when the text document changes
*/
workspace.onDidChangeTextDocument(() => {
if (!activeTextEditor) {
return;
}
triggerHighlight();
});
/**
* Trigger for when the window state changes
*/
window.onDidChangeWindowState((editor) => {
if (!editor) {
return;
}
triggerHighlight();
});
/**
* Trigger for when configuration changes
*/
workspace.onDidChangeConfiguration((editor) => {
if (!editor) {
return;
}
highlightStyle.dispose(); // Dump existing styling
isEnabled = getHighlighterStatus(); // check if line highlighter is enable/disabled
multiLineIsEnabled = getMultiLineHighlighterStatus(); // Check if multiline highlighting is enabled/disabled
highlightStyle = getHighlighterStyle(); // get new line highlighter styling
triggerHighlight(); // trigger highlight with new styling
});
/**
* main function that triggers the highlights
*/
function triggerHighlight(): void {
if (!activeTextEditor) {
return;
}
/**
* Sets the activeTextEditor to the current active window
*/
activeTextEditor = window.activeTextEditor;
if (activeTextEditor !== undefined) {
/**
* If the line highlighter function is enabled
* set the decorations with our chosen highlighting style on the selection
* otherwise (highlighter is disabled) dump our highlighting style
*/
switch (isEnabled) {
case true: /* isEnabled is true */
switch (multiLineIsEnabled) {
case true: /* isEnabled is true and multiLineIsEnabled is true */
activeTextEditor.setDecorations(highlightStyle, activeTextEditor.selections);
break;
case false: /* isEnabled is true and multiLineIsEnabled is false */
switch (activeTextEditor.selection.isSingleLine) {
case true: /* isEnabled is true and multiLineIsEnabled is false and VSCode is reporting a single line */
let currentPosition = [];
for (let i = 0; i < activeTextEditor.selections.length; i++) {
currentPosition[i] = { range: new Range(activeTextEditor.selections[i].anchor, activeTextEditor.selections[i].anchor) };
}
activeTextEditor.setDecorations(highlightStyle, currentPosition);
break;
case false: /* isEnabled is true and multiLineIsEnabled is false and VSCode is reporting multiple lines */
// Dispose of our highlighting style so multiple lines aren't all highlighted when clicking and dragging to highlight
activeTextEditor.setDecorations(highlightStyle, []); // This will dispose of a single editor instead of all editors
break;
default: /* isEnabled is true and multiLineIsEnabled is false and VSCode is reporting something else - break out of 3rd switch */
break;
}
break;
default: /* isEnabled is true and multiLineIsEnabled is undetected - break out of 2nd switch statement */
break;
}
break;
case false: /* isEnabled is false */
highlightStyle.dispose();
break;
default: /* break out of initial switch if 'true or false' is not found */
break;
}
// Keep track of position
new Position(activeTextEditor.selection.start.line, activeTextEditor.selection.start.character);
}
}
/**
* * Function to get the user configured highlighting styles, or use defaults
*
* * Designed with user configuration in mind, able to control different sides
* * independently from each other (in most cases). This allows for many different
* * configurations.
*
* ? Colors Can be input with the following values:
* * https://www.w3schools.com/cssref/css_colors.asp for string based color values
* * Hex -> #<value> | rgb(###, ###, ###) | rgba(###, ###, ###, ###) | hsla(##, ##%, ##%, .#)
*
* ? Width Input Values
* ! Some work better than others, if one isn't working try a different method:
* * thin | medium | thick | px | rem | em | cm
*
* ? Other values
* * font-style : none|normal|italic|oblique;
* * font-weight : none|normal|bold|bolder|lighter|number;
* * border-style : none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset;
* * outline-style : none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset;
* * outline-width : none|medium|thin|thick|length;
* * border-width : none|medium|thin|thick|length;
* ? https://www.w3schools.com/cssref/pr_text_text-decoration.asp for text-decoration
*
* ! borderWidthRight acts weirdly, on my system 16px works best with the other directions set to 1px
*
* @returns highlighterStyle
*/
function getHighlighterStyle(): TextEditorDecorationType {
// Used so we don't have to type out workspace.getConfiguration('mind-reader.lineHighlighter') on every line, ie: shorthand
const userConfig: WorkspaceConfiguration = workspace.getConfiguration('mind-reader.lineHighlighter');
const borderWidthTop : string = userConfig.get('borderWidthTop') || "1px";
const borderWidthRight : string = userConfig.get('borderWidthRight') || "16px";
const borderWidthBottom : string = userConfig.get('borderWidthBottom') || "1px";
const borderWidthLeft : string = userConfig.get('borderWidthLeft') || "1px";
const borderStyleTop : string = userConfig.get('borderStyleTop') || "solid";
const borderStyleRight : string = userConfig.get('borderStyleRight') || "solid";
const borderStyleBottom : string = userConfig.get('borderStyleBottom') || "solid";
const borderStyleLeft : string = userConfig.get('borderStyleLeft') || "solid";
const borderColorTop : string = userConfig.get('borderColorTop') || "#FFFFFF";
const borderColorRight : string = userConfig.get('borderColorRight') || "#FFFFFF";
const borderColorBottom : string = userConfig.get('borderColorBottom') || "#FFFFFF";
const borderColorLeft : string = userConfig.get('borderColorLeft') || "#FFFFFF";
const backgroundColor : string = userConfig.get('backgroundColor') || "#232C5C";
const fontStyle : string = userConfig.get('fontStyle') || "normal";
const fontWeight : string = userConfig.get('fontWeight') || "bolder";
const outlineColor : string = userConfig.get('outlineColor') || "#4866FE";
const outlineStyle : string = userConfig.get('outlineStyle') || "solid";
const outlineWidth : string = userConfig.get('outlineWidth') || "1px";
const textDecoration : string = userConfig.get('textDecoration') || "none";
const textColor : string = userConfig.get('textColor') || "#FFFFFF";
// Combine all our styling into a single variable to return
const highlighterStyle : TextEditorDecorationType = window.createTextEditorDecorationType({
isWholeLine : true,
backgroundColor : `${backgroundColor}`,
fontStyle : `${fontStyle}`,
fontWeight : `${fontWeight}`,
textDecoration : `${textDecoration}`,
color : `${textColor}`,
borderColor : `${borderColorTop} ${borderColorRight} ${borderColorBottom} ${borderColorLeft}`,
borderWidth : `${borderWidthTop} ${borderWidthRight} ${borderWidthBottom} ${borderWidthLeft}`,
borderStyle : `${borderStyleTop} ${borderStyleRight} ${borderStyleBottom} ${borderStyleLeft}`,
outlineColor : `${outlineColor}`,
outlineWidth : `${outlineWidth}`,
outlineStyle : `${outlineStyle}`,
});
// Return our variable
return highlighterStyle;
}
/**
* Function to retrieve the 'isEnabled' status
*
* This will determine if the line highlighter will display or not
* - enabled -> will show
* - disabled -> will not show
*
* @returns enabledStatus
*/
function getHighlighterStatus(): boolean | undefined {
// set a boolean variable
let enabledStatus: boolean | undefined;
/***
* if 'isEnabled' is missing from the settings (aka undefined)
* - set our variable to true (default)
* otherwise, 'isEnabled' is listed in the settings
* - so we just pull its value
*/
(workspace.getConfiguration('mind-reader.lineHighlighter').get('isEnabled') === undefined)
? (enabledStatus = true)
: (enabledStatus = workspace.getConfiguration('mind-reader.lineHighlighter').get('isEnabled'));
// return the enabledStatus
return enabledStatus;
}
function getMultiLineHighlighterStatus(): boolean | undefined {
// set a boolean variable
let multiLineIsEnabled: boolean | undefined;
/***
* if 'isEnabled' is missing from the settings (aka undefined)
* - set our variable to true (default)
* otherwise, 'isEnabled' is listed in the settings
* - so we just pull its value
*/
(workspace.getConfiguration('mind-reader.lineHighlighter').get('multiLineIsEnabled') === undefined)
? (multiLineIsEnabled = true)
: (multiLineIsEnabled = workspace.getConfiguration('mind-reader.lineHighlighter').get('multiLineIsEnabled'));
// return the enabledStatus
return multiLineIsEnabled;
}
}
// Clean-up after ourself
export function deactivate() {
// when the plugin is terminated remove all highlighting
if (highlightStyle !== undefined) {
highlightStyle.dispose();
}
}

View File

@ -3,7 +3,7 @@ import { Symbol, EOFTOKEN, TabInfo } from './token';
type Rule = { type Rule = {
pattern: RegExp, pattern: RegExp,
type: Symbol, type : Symbol,
}; };
/** /**
@ -11,8 +11,7 @@ type Rule = {
* The first item is a recognition pattern, used to recognize the token * The first item is a recognition pattern, used to recognize the token
* the second item is the token type * the second item is the token type
*/ */
const rules: Rule[] = [ const rules: Rule[] = [{
{
pattern: /^\s*def\s+(?<attr>[a-zA-Z_][a-zA-Z0-9_]*)\(/, pattern: /^\s*def\s+(?<attr>[a-zA-Z_][a-zA-Z0-9_]*)\(/,
type: Symbol.FUNCTION type: Symbol.FUNCTION
}, },
@ -56,52 +55,44 @@ const rules: Rule[] = [
pattern: /^\s*with\s+(?<attr>[^:]+):\s*$/, pattern: /^\s*with\s+(?<attr>[^:]+):\s*$/,
type: Symbol.WITH type: Symbol.WITH
}, },
{
pattern: /^\s*#+\s*(?<attr>.*)\s*$/,
type: Symbol.COMMENT
},
{
pattern: /^\s*$/,
type: Symbol.EMPTY
},
{
pattern: /^\s*(?<attr>[^#]+)+\s*$/,
type: Symbol.STATEMENT
}
]; ];
/** /**
* Line-By-Line Lexer * Line-By-Line Lexer
*/ */
export default class Lexer { export default class Lexer {
private textLines: string[] = []; // array of text lines private textLines : string[] = []; // array of text lines
private pos: number = 0; private pos : number = 0;
private _currToken: LineToken = EOFTOKEN; private _currToken: LineToken = EOFTOKEN;
/**
* Calculates indentation level for a line. If using soft tabs,
* indent level rounds up (so, tabSize+1 spaces is 2 levels,
* 2*tabSize+1 is 3, etc.)
*
* @param `text` The line of text.
* @param `tabFmt` A tab information descriptor.
* @return The indent of `text` with consideration for `tabFmt`.
*/
static getIndent(text: string, tabFmt: TabInfo): number {
let leadingSpace: number = text.length - text.trimLeft().length;
let indent: number;
if (tabFmt.hard) {
// used tabs
indent = leadingSpace;
} else {
// use spaces
indent = Math.ceil(leadingSpace/tabFmt.size!);
}
return indent;
}
/** /**
* @param `text` The text to lex. * @param `text` The text to lex.
* @param `tabFmt` A tab information descriptor * @param `tabFmt` A tab information descriptor
*/ */
constructor(text?: string, private tabFmt?: TabInfo) { constructor(text ? : string, private tabFmt ? : TabInfo) {
// default is 4 wide expanded tabs // default is 4 wide expanded tabs
this.tabFmt = { this.tabFmt = {
...{size: 4, hard: false}, ...{
size: 4,
hard: false
},
...tabFmt ...tabFmt
}; };
if (text) { if (text) {
// normalize linefeeds // normalize line feeds
text = text.replace('\r\n', '\n'); text = text.replace('\r\n', '\n');
} }
this.restart(text); this.restart(text);
@ -112,7 +103,7 @@ export default class Lexer {
* *
* @param `text` The new text to lex. * @param `text` The new text to lex.
*/ */
restart(text?: string): void { restart(text ? : string): void {
this.pos = 0; this.pos = 0;
this._currToken = EOFTOKEN; // if no input, already on EOFTOKEN this._currToken = EOFTOKEN; // if no input, already on EOFTOKEN
if (text) { if (text) {
@ -124,7 +115,9 @@ export default class Lexer {
/** /**
* @return the current {@link LineToken}. * @return the current {@link LineToken}.
*/ */
currToken(): LineToken { return this._currToken; } currToken(): LineToken {
return this._currToken;
}
/** /**
* Advance the position in the token stream. * Advance the position in the token stream.
@ -138,12 +131,13 @@ export default class Lexer {
// Until a LineToken is found, or EOF // Until a LineToken is found, or EOF
while (this.pos < this.textLines.length) { while (this.pos < this.textLines.length) {
let line: string = this.textLines[this.pos]; const line : string = this.textLines[this.pos];
let indent: number = Lexer.getIndent(line, this.tabFmt!); const indent: number = Lexer.getIndent(line, this.tabFmt!);
let token: LineToken; let token : LineToken;
for (var r of rules) { for (var r of rules) {
// Does line match pattern? // Does line match pattern?
let match: RegExpMatchArray | null = line.match(r.pattern); const match: RegExpMatchArray | null = line.match(r.pattern);
if (match) { if (match) {
// Yes... // Yes...
if (match.groups) { if (match.groups) {
@ -154,28 +148,22 @@ export default class Lexer {
this._currToken = token; this._currToken = token;
this.pos++; this.pos++;
return this.currToken(); return this.currToken();
} }
} }
// No rules matched // No rules matched
token = new LineToken(Symbol.INVALID, this.pos, 999999);
// TODO: move to rules
if (/^\s*(#.*)?$/.test(line)) {
// "empty" line
token = new LineToken(Symbol.EMPTY, this.pos, 999999);
} else {
// This is an INDENT token
token = new LineToken(Symbol.INDENT, this.pos, indent);
}
this._currToken = token; this._currToken = token;
this.pos++; this.pos++;
return this.currToken(); return this.currToken();
} }
// Didn't return, must be EOF // Didn't return, must be EOF
this._currToken = EOFTOKEN; this._currToken = EOFTOKEN;
this.pos++; this.pos++;
return this.currToken(); return this.currToken();
} }
@ -210,6 +198,61 @@ export default class Lexer {
} }
c--; c--;
} }
return this.next(); return this.next();
} }
/**
* Calculates indentation level for a line. If using soft tabs,
* indent level rounds up (so, tabSize+1 spaces is 2 levels,
* 2*tabSize+1 is 3, etc.)
*
* @param `text` The line of text.
* @param `tabFmt` A tab information descriptor.
* @return The indent of `text` with consideration for `tabFmt`.
*/
static getIndent(text: string, tabFmt: TabInfo): number {
const leadingSpace: number = text.length - text.trimStart().length;
let indent: number;
if (tabFmt.hard) {
// used tabs
indent = leadingSpace;
}
else {
// used spaces
//? indent = Math.round(leadingSpace / tabFmt.size! * 10) / 10; // fractional indentation support?
indent = Math.ceil(leadingSpace / tabFmt.size!);
}
return indent;
}
/**
* Calculates leading spaces for a line.
* This method uses arithmetic to calculate the number of leading spaces
*
* @param `line` The line of text.
* @return The number of leading spaces of `text`.
*/
static getLeadingSpacesByArithmetic(line: any): number {
const leadingSpaces: number = line.text.length - line.text.trimStart().length;
return leadingSpaces;
}
/**
* Calculates leading spaces for a line.
* This method finds the index position of the first non-whitespace character
* Since the index is built using a 0-index, the position of this character
* will equal the number of spaces preceding the character.
*
* @param `text` The line of text.
* @return The number of leading spaces of `text` with respect to the index position of the first non-whitespace character.
*/
static getLeadingSpacesByIndex(text: any): number {
const indexNum: number = text.firstNonWhitespaceCharacterIndex;
return indexNum;
}
} }

View File

@ -74,8 +74,9 @@ export default class Parser {
return children; return children;
} }
if (this.lexer.currToken().type === Symbol.INDENT || if (this.lexer.currToken().type === Symbol.STATEMENT ||
this.lexer.currToken().type === Symbol.EMPTY) { this.lexer.currToken().type === Symbol.EMPTY ||
this.lexer.currToken().type === Symbol.INVALID) {
const label = this.lexer.currToken().type; const label = this.lexer.currToken().type;
// regular code, advance and stay in same block // regular code, advance and stay in same block
children.push(new LexNode( children.push(new LexNode(

View File

@ -16,8 +16,10 @@ export enum Symbol {
EXCEPT = "except", EXCEPT = "except",
FINALLY = "finally", FINALLY = "finally",
WITH = "with", WITH = "with",
INDENT = "INDENT", // Indent token, default if not EOF, only contains indent information STATEMENT = "statement", // Indent token, contains non-empty code lines
COMMENT = "Comment",
EMPTY = "EMPTY", // empty line, used only to associate with the previous line EMPTY = "EMPTY", // empty line, used only to associate with the previous line
INVALID = "INVALID",
EOF = "EOF" EOF = "EOF"
} }
@ -53,7 +55,7 @@ export default class LineToken {
* @return A string representation of the token * @return A string representation of the token
*/ */
toString(): string { toString(): string {
return this.type + ", linenr:" + (this.linenr+1) + ", indentLevel: " + this.indentLevel + ", attr: " + this.attr; return `${this.type}, linenr: ${this.linenr+1}, indentLevel: ${this.indentLevel}, attr: ${this.attr}`;
} }
} }

View File

@ -12,7 +12,7 @@ suite('Lexer Test Suite', () => {
}); });
test('Empty String', () => { test('Empty String', () => {
let l: Lexer = new Lexer(undefined); let l: Lexer = new Lexer("");
assert.deepStrictEqual(l.currToken(), EOFTOKEN); assert.deepStrictEqual(l.currToken(), EOFTOKEN);
}); });
@ -22,18 +22,19 @@ suite('Lexer Test Suite', () => {
}); });
test('Whitespace', () => { test('Whitespace', () => {
let l: Lexer = new Lexer(' \t\t'.repeat(4).repeat(4)); let s: string = ' '.repeat(4).repeat(4);
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, 999999)); let l: Lexer = new Lexer(s);
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, s.length/4));
}); });
test('Comment', () => { test('Comment', () => {
let l: Lexer = new Lexer('# ur mom eats toes'); let l: Lexer = new Lexer('# this is a comment');
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, 999999)); assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.COMMENT, 0, 0, 'this is a comment'));
}); });
test('Non-Whitespace with no construct', () => { test('Non-Whitespace with no construct', () => {
let l: Lexer = new Lexer('foobar'); let l: Lexer = new Lexer('foobar');
assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.INDENT, 0, 0)); assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.STATEMENT, 0, 0, 'foobar'));
}); });
test('getIndent() accuracy, spaces', () => { test('getIndent() accuracy, spaces', () => {
@ -50,11 +51,14 @@ suite('Lexer Test Suite', () => {
} }
}); });
test('getIndent() accuracy, spaces with incomplete tab', () => { test('getIndent() accuracy, spaces with incomplete indentation', () => {
let size: number = 4;
for (var i = 0; i < 100; i++) { for (var i = 0; i < 100; i++) {
for (var j = 1; j <= 3; j++) { for (var j = 1; j <= 3; j++) {
let l: Lexer = new Lexer(' '.repeat(i) + ' '.repeat(j) + 'foobar', {size: 4, hard: false}); let l: Lexer = new Lexer(' '.repeat(i) + ' '.repeat(j) + 'foobar', {size: size, hard: false});
assert.strictEqual(l.currToken().indentLevel, i+1); // TODO: Swap these out when fractional indentation is used
//assert.strictEqual(l.currToken().indentLevel, i + (Math.round(j / size * 100) / 100));
assert.strictEqual(l.currToken().indentLevel, i + 1);
} }
} }
}); });
@ -150,7 +154,7 @@ suite('Lexer Test Suite', () => {
test('retract() 1-100', () => { test('retract() 1-100', () => {
let lines: string[] = Array.from(Array(100), (_, i) => 'line' + i); let lines: string[] = Array.from(Array(100), (_, i) => 'line' + i);
let reference: LineToken[] = lines.map((_, i) => { let reference: LineToken[] = lines.map((_, i) => {
return new LineToken(Symbol.INDENT, i, 0); return new LineToken(Symbol.STATEMENT, i, 0, `line${i}`);
}); });
for (var i = 0; i < 100; i++) { for (var i = 0; i < 100; i++) {
@ -169,7 +173,7 @@ suite('Lexer Test Suite', () => {
test('2 full lex and retract passes', () => { test('2 full lex and retract passes', () => {
let lines: string[] = Array.from(Array(100), (_, i)=> 'line' + i); let lines: string[] = Array.from(Array(100), (_, i)=> 'line' + i);
let reference: LineToken[] = lines.map((_, i) => { let reference: LineToken[] = lines.map((_, i) => {
return new LineToken(Symbol.INDENT, i, 0); return new LineToken(Symbol.STATEMENT, i, 0, `line${i}`);
}); });
let l: Lexer = new Lexer(lines.join('\n')); let l: Lexer = new Lexer(lines.join('\n'));

View File

@ -13,7 +13,7 @@ suite('LexNode Test Suite', () => {
}); });
test('children() of leaf', () => { test('children() of leaf', () => {
let n: LexNode = new LexNode('leafLexNode', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.INDENT, 0, 0)); let n: LexNode = new LexNode('leafLexNode', vscode.TreeItemCollapsibleState.None, new LineToken(Symbol.STATEMENT, 0, 0));
assert.strictEqual(n.children(), null); assert.strictEqual(n.children(), null);
}); });

View File

@ -7,7 +7,7 @@ import LexNode from '../../../pylex/node';
import LineToken from '../../../pylex/token'; import LineToken from '../../../pylex/token';
import { Symbol } from '../../../pylex/token'; import { Symbol } from '../../../pylex/token';
import { root,indent,empty } from '../../util'; import { root, statement } from '../../util';
/** /**
* Test Descriptor * Test Descriptor
@ -23,23 +23,24 @@ const tests: ParserTest[] = [
{ name: 'Single Empty Line', input: [''], output: root(null) }, { name: 'Single Empty Line', input: [''], output: root(null) },
{ {
name: 'Single Whitespace Only Line', name: 'Single Whitespace Only Line',
input: [' '], input: [' '], // length 20
output: root([ output: root([
empty(0) new LexNode(Symbol.EMPTY, 0, new LineToken(Symbol.EMPTY, 0, 20 / 4)) // 4 spaces per indent
]) ])
}, },
{ {
name: 'Single Comment Only Line', name: 'Single Comment Only Line',
input: ['# ur mom likes peas'], input: ['# this is a comment'],
output: root([ output: root([
empty(0) new LexNode(Symbol.EMPTY, 0, new LineToken(Symbol.COMMENT, 0, 0, "this is a comment"))
]) ])
}, },
{ {
name: 'Single Non-Control Line', name: 'Single Non-Control Line',
input: ['my_age = 42'], input: ['my_age = 42'],
output: root([ output: root([
indent(0, 0) //statement(0, 0)
new LexNode("name", 0, new LineToken(Symbol.STATEMENT, 0, 0, 'my_age = 42'))
]) ])
}, },
{ {
@ -55,7 +56,10 @@ const tests: ParserTest[] = [
'bar = "Blue M&Ms make me happy <:)"', 'bar = "Blue M&Ms make me happy <:)"',
'reba = "A hard working gal"' 'reba = "A hard working gal"'
], ],
output: root([indent(0,0), indent(1,0)]), output: root([
new LexNode("name", 0, new LineToken(Symbol.STATEMENT, 0, 0, 'bar = "Blue M&Ms make me happy <:)"')),
new LexNode("name", 0, new LineToken(Symbol.STATEMENT, 1, 0, 'reba = "A hard working gal"'))
]),
}, },
{ {
@ -66,11 +70,13 @@ const tests: ParserTest[] = [
'billy = "Scrubbly Bubbles!"' 'billy = "Scrubbly Bubbles!"'
], ],
output: root([ output: root([
new LexNode('if radioshack', new LexNode('if radioshack', 0,
vscode.TreeItemCollapsibleState.None,
new LineToken(Symbol.IF, 0, 0, 'radioshack'), new LineToken(Symbol.IF, 0, 0, 'radioshack'),
[indent(1, 1)]), [
indent(2, 0) new LexNode('print radioshack.hours', 0, new LineToken(Symbol.STATEMENT, 1, 1, 'print radioshack.hours'))
]
),
new LexNode('billy = "Scrubbly Bubbles!"', 0, new LineToken(Symbol.STATEMENT, 2, 0, 'billy = "Scrubbly Bubbles!"'))
]) ])
}, },
@ -82,11 +88,12 @@ const tests: ParserTest[] = [
' print radioshack.hours' ' print radioshack.hours'
], ],
output: root([ output: root([
indent(0, 0), new LexNode('billy = "Scrubbly Bubbles!"', 0, new LineToken(Symbol.STATEMENT, 0, 0, 'billy = "Scrubbly Bubbles!"')),
new LexNode('if radioshack', new LexNode('if radioshack', 0, new LineToken(Symbol.IF, 1, 0, 'radioshack'),
vscode.TreeItemCollapsibleState.None, [
new LineToken(Symbol.IF, 1, 0, 'radioshack'), new LexNode('print radioshack.hours', 0, new LineToken(Symbol.STATEMENT, 2, 1, 'print radioshack.hours'))
[indent(2, 1)]) ]
)
]) ])
}, },
@ -101,15 +108,18 @@ const tests: ParserTest[] = [
' print("You really eat this?")', ' print("You really eat this?")',
], ],
output: root([ output: root([
new LexNode('if yummy', new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
vscode.TreeItemCollapsibleState.None, [
new LineToken(Symbol.IF, 0, 0, 'yummy'), [indent(1, 1)]), statement(1, 1, 'print("HOoray!")')
new LexNode('elif just_ok', ]),
vscode.TreeItemCollapsibleState.None, new LexNode('elif just_ok', 0, new LineToken(Symbol.ELIF, 2, 0, 'just_ok'),
new LineToken(Symbol.ELIF, 2, 0, 'just_ok'), [indent(3, 1)]), [
new LexNode('else', statement(3, 1, 'print("Do you have anything else?")')
vscode.TreeItemCollapsibleState.None, ]),
new LineToken(Symbol.ELSE, 4, 0), [indent(5,1)]), new LexNode('else', 0, new LineToken(Symbol.ELSE, 4, 0),
[
statement(5, 1, 'print("You really eat this?")')
]),
]) ])
}, },
@ -122,13 +132,14 @@ const tests: ParserTest[] = [
], ],
output: root([ output: root([
new LexNode('if yummy', new LexNode('if yummy',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.IF, 0, 0, 'yummy'), new LineToken(Symbol.IF, 0, 0, 'yummy'),
[ [
new LexNode('if in_my_tummy', new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
vscode.TreeItemCollapsibleState.None, [
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'), statement(2, 2, 'exclaim("Scrumdiddlyumptious!")')
[indent(2, 2)]) ]
)
], ],
) )
]) ])
@ -141,23 +152,20 @@ const tests: ParserTest[] = [
' if in_my_tummy:', ' if in_my_tummy:',
' exclaim("Scrumdiddlyumptious!")', ' exclaim("Scrumdiddlyumptious!")',
'else:', 'else:',
' exclaim("DAESGUSTEN~)"' ' exclaim("DISGUSTING!")'
], ],
output: root([ output: root([
new LexNode('if yummy', new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
vscode.TreeItemCollapsibleState.None,
new LineToken(Symbol.IF, 0, 0, 'yummy'),
[ [
new LexNode('if in_my_tummy', new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
vscode.TreeItemCollapsibleState.None, [
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'), statement(2, 2, 'exclaim("Scrumdiddlyumptious!")')
[indent(2, 2)]) ]
)
] ]
), ),
new LexNode('else', new LexNode('else', 0, new LineToken(Symbol.ELSE, 3, 0),
vscode.TreeItemCollapsibleState.None, [statement(4, 1, 'exclaim("DISGUSTING!")')]
new LineToken(Symbol.ELSE, 3, 0),
[indent(4, 1)]
) )
]) ])
}, },
@ -168,31 +176,27 @@ const tests: ParserTest[] = [
'if yummy:', 'if yummy:',
' if in_my_tummy:', ' if in_my_tummy:',
' if looks_like_a_mummy:', ' if looks_like_a_mummy:',
' print("you have a spot on your tummy"', ' print("you have a spot on your tummy")',
'else:', 'else:',
' print("Food is food...")' ' print("Food is food...")'
], ],
output: root([ output: root([
new LexNode('if yummy', new LexNode('if yummy',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.IF, 0, 0, 'yummy'), new LineToken(Symbol.IF, 0, 0, 'yummy'),
[ [
new LexNode('if in_my_tummy', new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
vscode.TreeItemCollapsibleState.None,
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
[ [
new LexNode('if looks_like_a_mummy', new LexNode('if looks_like_a_mummy', 0, new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'),
vscode.TreeItemCollapsibleState.None, [statement(3, 3, 'print("you have a spot on your tummy")')])
new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'),
[indent(3, 3)])
] ]
) )
] ]
), ),
new LexNode('else', new LexNode('else',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.ELSE, 4, 0), new LineToken(Symbol.ELSE, 4, 0),
[indent(5, 1)] [statement(5, 1, 'print("Food is food...")')]
) )
]) ])
}, },
@ -203,44 +207,42 @@ const tests: ParserTest[] = [
'if yummy:', 'if yummy:',
' if in_my_tummy:', ' if in_my_tummy:',
' if looks_like_a_mummy:', ' if looks_like_a_mummy:',
' print("you have a spot on your tummy"', ' print("you have a spot on your tummy")',
' else:', ' else:',
' print("eek! a zombie!)', ' print("eek! a zombie!")',
' elif in_my_mouth:', ' elif in_my_mouth:',
' print("itll be in my tummy soon!"', ' print("itll be in my tummy soon!")',
'else:', 'else:',
' print("Food is food...")' ' print("Food is food...")'
], ],
output: root([ output: root([
new LexNode('if yummy', new LexNode('if yummy', 0,
vscode.TreeItemCollapsibleState.None,
new LineToken(Symbol.IF, 0, 0, 'yummy'), new LineToken(Symbol.IF, 0, 0, 'yummy'),
[ [
new LexNode('if in_my_tummy', new LexNode('if in_my_tummy', 0,
vscode.TreeItemCollapsibleState.None,
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'), new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
[ [
new LexNode('if looks_like_a_mummy', new LexNode('if looks_like_a_mummy',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'), new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'),
[indent(3, 3)]), [statement(3, 3, 'print("you have a spot on your tummy")')]),
new LexNode('else', new LexNode('else',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.ELSE, 4, 2), new LineToken(Symbol.ELSE, 4, 2),
[indent(5, 3)]) [statement(5, 3, 'print("eek! a zombie!")')])
] ]
), ),
new LexNode('elif in_my_mouth', new LexNode('elif in_my_mouth',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.ELIF, 6, 1, 'in_my_mouth'), new LineToken(Symbol.ELIF, 6, 1, 'in_my_mouth'),
[indent(7, 2)] [statement(7, 2, 'print("itll be in my tummy soon!")')]
) )
] ]
), ),
new LexNode('else', new LexNode('else',
vscode.TreeItemCollapsibleState.None, 0,
new LineToken(Symbol.ELSE, 8, 0), new LineToken(Symbol.ELSE, 8, 0),
[indent(9, 1)] [statement(9, 1, 'print("Food is food...")')]
) )
]) ])
}, },
@ -248,21 +250,24 @@ const tests: ParserTest[] = [
name: 'Multiline Block', name: 'Multiline Block',
input: [ input: [
'if yummy:', 'if yummy:',
' print("you have a spot on your tummy"', ' print("you have a spot on your tummy")',
' print("eek! a zombie!)', ' print("eek! a zombie!)',
' print("itll be in my tummy soon!"', ' print("itll be in my tummy soon!")',
'else:', 'else:',
' print("Food is food...")' ' print("Food is food...")'
], ],
output: root([ output: root([
new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'), new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
[ [
indent(1, 1), statement(1, 1, 'print("you have a spot on your tummy")'),
indent(2, 1), statement(2, 1, 'print("eek! a zombie!)'),
indent(3, 1), statement(3, 1, 'print("itll be in my tummy soon!")'),
] ]
), ),
new LexNode('else', 0, new LineToken(Symbol.ELSE, 4, 0), [indent(5, 1)]) new LexNode('else', 0, new LineToken(Symbol.ELSE, 4, 0),
[
statement(5, 1, 'print("Food is food...")')
])
]) ])
} }
]; ];

View File

@ -50,19 +50,19 @@ function root(nodes: LexNode[] | null): LexNode {
} }
/* short hand for returning an indentation token for a certain line and indentation */ /* short hand for returning an indentation token for a certain line and indentation */
function indent(linenr: number, indentLevel: number): LexNode { function statement(linenr: number, indentLevel: number, text: string = ""): LexNode {
return new LexNode('INDENT', 0, new LineToken(PylexSymbol.INDENT, linenr, indentLevel)); return new LexNode(PylexSymbol.STATEMENT, 0, new LineToken(PylexSymbol.STATEMENT, linenr, indentLevel, text));
} }
/* short hand for returning an empty token for a certain line*/ /* short hand for returning an empty token for a certain line*/
function empty(linenr: number): LexNode { function empty(linenr: number): LexNode {
return new LexNode('EMPTY', 0, new LineToken(PylexSymbol.EMPTY, linenr, 999999)); return new LexNode(PylexSymbol.EMPTY, 0, new LineToken(PylexSymbol.EMPTY, linenr, 999999));
} }
export { export {
deparent, deparent,
root, root,
indent, statement as statement,
empty empty
}; };