diff --git a/.github/workflows/vscode-test.yaml b/.github/workflows/vscode-test.yaml
index 3e11b5e..143c952 100644
--- a/.github/workflows/vscode-test.yaml
+++ b/.github/workflows/vscode-test.yaml
@@ -10,15 +10,22 @@ jobs:
build:
strategy:
matrix:
- os: [macos-11, ubuntu-latest, windows-latest]
+ os:
+ - macos-11
+ - ubuntu-latest
+ - windows-latest
+ node_version:
+ - 16
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install Node.js
- uses: actions/setup-node@v1
+ uses: actions/setup-node@v3
with:
- node-version: 10.x
+ node-version: ${{ matrix.node_version }}
+ cache: 'npm'
+ cache-dependency-path: '**/package-lock.json'
- run: npm ci
- run: xvfb-run -a npm test
if: runner.os == 'Linux'
diff --git a/.gitignore b/.gitignore
index 0b60dfa..ba4285a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ out
dist
node_modules
.vscode-test/
+*.code-workspace
*.vsix
diff --git a/README.md b/README.md
index 2239951..504d36e 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
-Mind_Reader
+Mind Reader
@@ -42,7 +42,7 @@ Python programming with LEGO Mindstorms. Our goal is to:
-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:
@@ -73,7 +73,7 @@ The electron version should be listed, e.g.: `Electron: 13.5.2`
-### 3 Finding the Mind_Reader extension directory
+### 3 Finding the Mind Reader extension directory
On MacOS and Linux this is `~/.vscode/extensions`.
On Windows this is `C:\\.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.
@@ -102,8 +102,8 @@ $ electron-rebuild --version=ELECTRON_VERSION
Use the following to set up the extension for development.
```console
-$ git clone https://github.com/SingleSemesterSnobs/Mind_Reader.git
-$ cd Mind_Reader
+$ git clone https://github.com/SingleSemesterSnobs/Mind Reader.git
+$ cd Mind Reader
$ npm install
```
diff --git a/media/html/main.html b/media/html/main.html
index afd1508..27fe0e6 100644
--- a/media/html/main.html
+++ b/media/html/main.html
@@ -8,7 +8,7 @@
- Welcome to Mind_Reader!
+ Welcome to Mind Reader!
We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.
-
diff --git a/package-lock.json b/package-lock.json
index 82ed2a5..77d24c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "mind-reader",
- "version": "0.0.1",
+ "version": "2.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "mind-reader",
- "version": "0.0.1",
+ "version": "2.0.0",
"dependencies": {
"serialport": "^9.2.5"
},
@@ -26,7 +26,7 @@
"vscode-test": "^1.5.2"
},
"engines": {
- "vscode": "^1.60.0"
+ "vscode": "^1.66.0"
}
},
"node_modules/@babel/code-frame": {
@@ -2101,9 +2101,9 @@
}
},
"node_modules/minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"node_modules/mkdirp": {
"version": "0.5.5",
@@ -2817,9 +2817,9 @@
]
},
"node_modules/simple-get": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
- "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
+ "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@@ -3205,9 +3205,9 @@
}
},
"node_modules/wide-align/node_modules/ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
"engines": {
"node": ">=4"
}
@@ -4896,9 +4896,9 @@
}
},
"minimist": {
- "version": "1.2.5",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
- "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
},
"mkdirp": {
"version": "0.5.5",
@@ -5405,9 +5405,9 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
},
"simple-get": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
- "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
+ "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"requires": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
@@ -5715,9 +5715,9 @@
},
"dependencies": {
"ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
diff --git a/package.json b/package.json
index 457e219..67996e2 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,56 @@
{
"name": "mind-reader",
- "displayName": "Mind_Reader",
- "repository": "https://github.com/SingleSemesterSnobs/Mind_Reader",
- "version": "1.0.0",
+ "displayName": "Mind Reader",
+ "homepage": "https://github.com/We-Dont-Byte/Mind_Reader/wiki",
+ "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": {
- "vscode": "^1.60.0"
+ "vscode": "^1.66.0"
},
"categories": [
"Other"
@@ -15,199 +61,249 @@
"main": "./out/extension.js",
"contributes": {
"commands": [
- {
- "command": "mind-reader.helloWorld",
- "title": "Hello World"
- },
{
"command": "mind-reader.increaseFontScale",
- "title": "Increase Font Scale"
+ "title": "Increase Font Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.decreaseFontScale",
- "title": "Decrease Font Scale"
+ "title": "Decrease Font Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.resetFontScale",
- "title": "Reset Font Scale"
+ "title": "Reset Font Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.increaseEditorScale",
- "title": "Increase Editor Scale"
+ "title": "Increase Editor Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.decreaseEditorScale",
- "title": "Decrease Editor Scale"
+ "title": "Decrease Editor Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.resetEditorScale",
- "title": "Reset Editor Scale"
+ "title": "Reset Editor Scale",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.selectTheme",
- "title": "Select Theme"
+ "title": "Select Theme",
+ "category": "Mind Reader"
},
{
"command": "mind-reader.openWebview",
"title": "Mind Reader Webview"
},
{
- "command": "mind-reader.openKeyBindWin",
- "title": "Key Bindings for Windows"
+ "command": "mind-reader.openKeybinds",
+ "title": "Edit Keybinds",
+ "category": "Mind Reader"
},
{
- "command": "mind-reader.openKeyBindMac",
- "title": "Key Bindings for Mac"
+ "command": "mind-reader.getLineScope",
+ "title": "Get Scope of the Current Line",
+ "category": "Mind Reader"
},
{
- "command": "mind-reader.runLineContext",
- "title": "Run Line Context"
+ "command": "mind-reader.getWordsUnderCursor",
+ "title": "Get Words Under the Cursor",
+ "category": "Mind Reader"
},
{
- "command": "mind-reader.runCursorContext",
- "title": "Run Cursor Context"
+ "command": "mind-reader.getLineNumber",
+ "title": "Get The Current Line Number"
},
{
"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",
- "title": "Connect LEGO Hub"
+ "title": "Connect LEGO SPIKE Prime Hub",
+ "category": "SPIKE Prime"
},
{
"command": "mind-reader.disconnectHub",
- "title": "Disconnect LEGO Hub"
+ "title": "Disconnect LEGO SPIKE Prime Hub",
+ "category": "SPIKE Prime"
},
{
"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",
- "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",
- "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",
- "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": [
{
- "command": "mind-reader.decreaseFontScale",
- "key": "numpad_subtract",
- "mac": "d"
+ "command": "editor.action.fontZoomOut",
+ "key": "Shift+Alt+z -",
+ "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
},
{
- "command": "mind-reader.increaseFontScale",
- "key": "numpad_add",
- "mac": "[NumpadAdd]"
+ "command": "editor.action.fontZoomIn",
+ "key": "Shift+Alt+z =",
+ "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
},
{
- "command": "mind-reader.increaseEditorScale",
- "key": "shift+numpad_add",
- "mac": "Shift+[NumpadAdd]"
- },
- {
- "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": "editor.action.fontZoomReset",
+ "key": "Shift+Alt+z 0",
+ "when": "editorTextFocus && config.mind-reader.reader.screenReader != JAWS"
},
{
"command": "mind-reader.getIndent",
- "key": "Shift+Tab",
- "mac": "Shift+[Tab]"
+ "key": "Ctrl+Shift+/ I",
+ "mac": "Cmd+Shift+[Slash] I",
+ "when": "editorTextFocus"
},
{
- "command": "mind-reader.openKeyBindWin",
- "key": "Ctrl+Shift+8",
- "mac": "Cmd+Shift+8"
+ "command": "mind-reader.getLeadingSpaces",
+ "key": "Ctrl+Shift+/ S",
+ "mac": "Cmd+Shift+[Slash] S",
+ "when": "editorTextFocus",
+ "comment": "Requires python language"
},
{
- "command": "mind-reader.openKeyBindMac",
- "key": "Ctrl+Shift+9",
- "mac": "Cmd+Shift+9"
+ "command": "mind-reader.getLineNumber",
+ "key": "Ctrl+Shift+/ L",
+ "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": {
- "editor/context": [
- {
- "submenu": "mind-reader.editor.context",
- "group": "mind-reader"
- }
- ],
+ "editor/context": [{
+ "submenu": "mind-reader.editor.context",
+ "group": "mind-reader"
+ }],
"mind-reader.editor.context": [
{
- "command": "mind-reader.increaseEditorScale",
+ "command": "mind-reader.selectTheme",
+ "group": "mind-reader",
+ "when": "activeEditor"
+ },
+ {
+ "command": "mind-reader.increaseFontScale",
"group": "mind-reader",
"when": "activeEditor"
},
@@ -237,7 +333,27 @@
"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",
"when": "activeEditor"
},
@@ -247,30 +363,25 @@
"when": "activeEditor"
},
{
- "command": "mind-reader.openKeyBindWin",
- "group": "mind-reader",
- "when": "activeEditor"
- },
- {
- "command": "mind-reader.openKeyBindMac",
+ "command": "mind-reader.openKeybinds",
"group": "mind-reader",
"when": "activeEditor"
}
]
},
- "submenus": [
- {
- "id": "mind-reader.editor.context",
- "label": "Mind_Reader"
- }
- ],
- "configuration": {
- "title": "Mind_Reader",
+ "submenus": [{
+ "id": "mind-reader.editor.context",
+ "label": "Mind Reader"
+ }],
+ "configuration": [{
+ "title": "Mind Reader",
+ "order": 0,
"properties": {
- "mindReader.productType": {
+ "mind-reader.productType": {
+ "order": 1,
"type": "string",
"description": "Specifies the LEGO® product.",
- "default": "MINDSTORMS EV3",
+ "default": "SPIKE Prime",
"enum": [
"MINDSTORMS EV3",
"SPIKE Prime"
@@ -280,40 +391,177 @@
"LEGO® Education SPIKE™ Prime Set (45678)"
]
},
- "mindReader.reader.screenReader": {
+ "mind-reader.reader.screenReader": {
+ "order": 0,
"type": "string",
"description": "Specifies which screen reader to optimize for.",
"default": "NVDA",
"enum": [
+ "JAWS",
"NVDA",
"Orca",
"VoiceOver"
],
"enumDescriptions": [
+ "Job Access With Speech (Windows)",
"NonVisual Desktop Access (Windows)",
"Orca (Linux)",
"Apple VoiceOver (macOS)"
]
},
- "mindReader.reader.contextWindow": {
+ "mind-reader.reader.contextWindow": {
+ "order": 3,
"type": "number",
"description": "The number of words around the cursor to use when reading the cursor context",
"default": 1
},
- "mindReader.connection.portPath": {
+ "mind-reader.connection.portPath": {
+ "order": 2,
"type": "string",
"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"
}
}
},
+ {
+ "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": [
- {
+ "MindReader": [{
"id": "accessActions",
"name": "Access Actions",
"icon": "media/dep.svg",
@@ -328,13 +576,11 @@
]
},
"viewsContainers": {
- "activitybar": [
- {
- "id": "MindReader",
- "title": "MindReader Actions",
- "icon": "media/dep.svg"
- }
- ]
+ "activitybar": [{
+ "id": "MindReader",
+ "title": "MindReader Actions",
+ "icon": "media/dep.svg"
+ }]
}
},
"scripts": {
diff --git a/setup-development/linux/install-linux.sh b/setup-development/linux/install-linux.sh
new file mode 100755
index 0000000..608223a
--- /dev/null
+++ b/setup-development/linux/install-linux.sh
@@ -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 .
\ No newline at end of file
diff --git a/setup-development/linux/package-managers/apt.dependencies b/setup-development/linux/package-managers/apt.dependencies
new file mode 100644
index 0000000..05070de
--- /dev/null
+++ b/setup-development/linux/package-managers/apt.dependencies
@@ -0,0 +1,4 @@
+apt-transport-https
+build-essential
+python3
+wget
diff --git a/setup-development/linux/package-managers/pacman.dependencies b/setup-development/linux/package-managers/pacman.dependencies
new file mode 100644
index 0000000..1b50f89
--- /dev/null
+++ b/setup-development/linux/package-managers/pacman.dependencies
@@ -0,0 +1,4 @@
+base-devel
+git
+wget
+python3
diff --git a/setup-development/linux/upgrade-linux.sh b/setup-development/linux/upgrade-linux.sh
new file mode 100755
index 0000000..1cfa484
--- /dev/null
+++ b/setup-development/linux/upgrade-linux.sh
@@ -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!"
diff --git a/setup-development/windows/install-windows.ps1 b/setup-development/windows/install-windows.ps1
new file mode 100644
index 0000000..dba9613
--- /dev/null
+++ b/setup-development/windows/install-windows.ps1
@@ -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)
+}
\ No newline at end of file
diff --git a/setup-development/windows/upgrade-windows.ps1 b/setup-development/windows/upgrade-windows.ps1
new file mode 100644
index 0000000..0e7a549
--- /dev/null
+++ b/setup-development/windows/upgrade-windows.ps1
@@ -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)
+}
\ No newline at end of file
diff --git a/setup-development/windows/winget/dependencies.json b/setup-development/windows/winget/dependencies.json
new file mode 100644
index 0000000..a20180d
--- /dev/null
+++ b/setup-development/windows/winget/dependencies.json
@@ -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"
+}
\ No newline at end of file
diff --git a/src/commands/hub.ts b/src/commands/hub.ts
index 1b8bd03..1b4ae63 100755
--- a/src/commands/hub.ts
+++ b/src/commands/hub.ts
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import * as path from 'path';
import HubManager from '../hubManager';
+//import EV3Manager from '../ev3Manager';
import { CommandEntry } from './commandEntry';
@@ -33,12 +34,23 @@ export const hubCommands: CommandEntry[] = [
{
name: 'mind-reader.deleteProgram',
callback: deleteProgram
- },
+ }/*,
+ {
+ name: 'mind-reader.ev3.test',
+ callback: ev3test
+ }*/
];
// Current connected hub
let hub: HubManager | null = null;
+/*
+let ev3: EV3Manager | null = null;
+async function ev3test(): Promise {
+ ev3 = await EV3Manager.activate();
+ ev3.test();
+}
+*/
async function connectHub(): Promise {
if (hub && hub.isOpen()) {
vscode.window.showWarningMessage('LEGO Hub is already connected, reconnecting...');
@@ -53,7 +65,7 @@ async function connectHub(): Promise {
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) {
let slots: vscode.QuickPickItem[] = [];
diff --git a/src/commands/nav.ts b/src/commands/nav.ts
index f694e40..e211f97 100755
--- a/src/commands/nav.ts
+++ b/src/commands/nav.ts
@@ -1,24 +1,19 @@
-import * as vscode from 'vscode';
-import * as fs from 'fs';
+import * as vscode from "vscode";
-import { CommandEntry } from './commandEntry';
+import { CommandEntry } from "./commandEntry";
export const navCommands: CommandEntry[] = [
{
name: 'mind-reader.openWebview',
callback: openWebview,
},
-
{
- name: 'mind-reader.openKeyBindWin',
- callback: () => openKeyBindWin('Windows')
- },
- {
- name: 'mind-reader.openKeyBindMac',
- callback: () => openKeyBindWin('Mac'),
+ name: "mind-reader.openKeybinds",
+ callback: () => vscode.commands.executeCommand("workbench.action.openGlobalKeybindings", "mind-reader"),
},
//Navigation Keys......
+ // TODO: Why is this here? Extensions can rebind existing keybinds.
{
name: 'mind-reader.showAllSymbols',
callback: () => vscode.commands.executeCommand('workbench.action.showAllSymbols'),
@@ -82,35 +77,58 @@ export const navCommands: CommandEntry[] = [
// COMMAND CALLBACK IMPLEMENTATIONS
function openWebview(): void {
- //vscode.commands.executeCommand('workbench.action.zoomOut');
const panel = vscode.window.createWebviewPanel(
- 'mindReader', // Identifies the type of the webview. Used internally
- 'Mind Reader', // Title of the panel displayed to the user
+ "mind-reader", // Identifies the type of the webview. Used internally
+ "Mind Reader", // 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.
- panel.webview.html = getWebviewContent('media/html/main.html');
+ panel.webview.html = getWebviewContent();
}
-function getWebviewContent(filepath: string) {
- return fs.readFileSync(filepath, {encoding: 'utf-8'});
+function getWebviewContent() {
+ return `
+
+
+
+
+ Mind Reader
+
+
+
+
+
Welcome to Mind Reader!
+ We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.
+
+ -
+ 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.
+
+ The tool has hotkeys for both PC and Mac commands.
+
+ - This system is intended for everyone, but primarily for students K-12 who are visually impaired.
+ -
+ Our goal is to provide an enhanced experience for students who are visually impaired that is transparent to
+ sighted students.
+
+ This allows for everyone to use the same software solution, whether or not they are
+ vision impaired.
+
+
+ Use the following key binding to bring up a page for all key bindings for windows
+
+ Control and Shift and 8
+
+ Use this key binding to do the same for mac computers:
+
+ Command and Shift and 9
+
+ This is the Lego Spike Prime!
+
+
+
+ Get the robot!
+
+ `;
}
-
-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');
- }
-}
-
-
diff --git a/src/commands/text.ts b/src/commands/text.ts
index 8e3beaf..b0b8b15 100755
--- a/src/commands/text.ts
+++ b/src/commands/text.ts
@@ -1,168 +1,388 @@
-import * as vscode from 'vscode';
-import * as pl from '../pylex';
-
-import { CommandEntry } from './commandEntry';
+"use strict";
+import pl = require("../pylex");
+import { CommandEntry } from './commandEntry';
+import { Position, Selection, TextEditor, TextLine, window, workspace } from "vscode";
export const textCommands: CommandEntry[] = [
- {
- name: 'mind-reader.getIndent',
- callback: getIndent,
- },
+ {
+ name: 'mind-reader.getLineNumber',
+ callback: getLineNumber,
+ },
+ {
+ name: 'mind-reader.getIndent',
+ callback: getIndent,
+ },
+ {
+ name: 'mind-reader.getLeadingSpaces',
+ callback: getLeadingSpaces,
+ },
+ {
+ name: 'mind-reader.selectLeadingWhitespace',
+ callback: selectLeadingWhitespace
+ },
+ {
+ name: 'mind-reader.getNumberOfSelectedLines',
+ callback: getNumberOfSelectedLines,
+ },
+ {
+ name: 'mind-reader.getLineScope',
+ callback: runLineContext,
+ },
+ {
+ name: 'mind-reader.getWordsUnderCursor',
+ callback: runCursorContext
+ }
+];
- {
- name: 'mind-reader.runLineContext',
- callback: runLineContext,
- },
+/** Helper Function
+ *
+ * @param editor
+ * @returns numSpaces
+ ** There are two methods that can be used to find the leading spaces:
+ ** method 1:
+ ** 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
+ ** 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;
- {
- name: 'mind-reader.runCursorContext',
- callback: runCursorContext
- }
-]
+ 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);
+ }
+
+ 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 {
- let editor = vscode.window.activeTextEditor;
- if(editor)
- {
- let lineNum = editor.selection.active.line + 1;
- let textLine = editor.document.lineAt(lineNum - 1);
- if(textLine.isEmptyOrWhitespace)
- {
- vscode.window.showInformationMessage("Line number " + lineNum.toString() + " Is Empty");
+ 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
+ const tabFmt: pl.TabInfo = {
+ size: typeof editor.options.tabSize === 'number'? editor.options.tabSize: 4,
+ hard: !editor.options.insertSpaces
+ };
+ const i: number = pl.Lexer.getIndent(line.text, tabFmt);
+
+ (i !== 1)
+ ? window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indents`)
+ : window.showInformationMessage(`Line ${lineNum.toString()}: ${i.toString()} indent`);
+ }
}
- else
- {
- // Grab tab format from open document
- let tabFmt = {
- size: editor.options.tabSize as number,
- hard: !editor.options.insertSpaces
- };
- let i = pl.Lexer.getIndent(textLine.text, tabFmt);
- vscode.window.showInformationMessage("Line Number " + lineNum.toString() + " Indentation " + i.toString());
+ else {
+ 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
}
- }
- else{
- vscode.window.showErrorMessage('No document currently active');
- }
}
function runLineContext(): void {
- let editor = vscode.window.activeTextEditor;
- if (editor){
- // current text and line number
- let editorText = editor.document.getText();
- let line = editor.selection.active.line;
+ const editor: TextEditor | undefined = window.activeTextEditor;
- // get tab info settings
- let size = parseInt(editor.options.tabSize as string);
- let hard = !editor.options.insertSpaces;
+ if (editor) {
+ // current text and line number
+ const editorText: string = editor.document.getText();
+ const line : number = editor.selection.active.line;
+ // get tab info settings
+ const size : number = typeof editor.options.tabSize === 'number'? editor.options.tabSize: 4;
+ const hard : boolean = !editor.options.insertSpaces;
+ // initialize parser
+ const parser : pl.Parser = new pl.Parser(editorText, {
+ size,
+ hard
+ });
- // initialize parser
- let parser = new pl.Parser(editorText, {size, hard});
- parser.parse();
+ parser.parse();
+ const context: pl.LexNode[] = parser.context(line);
+ // build text
+ const contentString: string = createContextString(context, line);
- let context = parser.context(line);
-
- // build text
- let contentString = createContextString(context, line);
- vscode.window.showInformationMessage(contentString);
- } else {
- vscode.window.showErrorMessage('No document currently active');
- }
+ window.showInformationMessage(contentString);
+ }
+ else {
+ window.showErrorMessage('No document currently active');
+ }
}
function createContextString(context: pl.LexNode[], line: number): string {
if (context.length < 1) {
- 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) {
- 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:""
+ } ${context[0].token.attr.toString()}`;
}
- for (let i = 1; i < context.length; i++) {
- let node = context[i];
- if (node.label === 'root') {
- // root
- contextString += ' in the Document Root';
- continue;
- }
- if (node.token!.type !== pl.PylexSymbol.EMPTY &&
- node.token!.type !== pl.PylexSymbol.INDENT) {
- contextString += ' inside ' + node.token!.type.toString();
- if (node.token!.attr) {
- contextString += ' ' + node.token!.attr.toString();
+ for (let i: number = 1; i < context.length; i++) {
+ const node: pl.LexNode = context[i];
+ const inside: string = "inside";
+ // Node contains information relevant to the current line
+ 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;
}
-// 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 {
- let editor = vscode.window.activeTextEditor;
- if (!editor) {
- vscode.window.showErrorMessage('RunCursorContext: No Active Editor');
- return;
- }
+ const editor: TextEditor | undefined = window.activeTextEditor;
- const cursorPos: vscode.Position = editor.selection.active;
- const text: string = editor.document.lineAt(cursorPos).text;
- const windowSize: number = vscode.workspace.getConfiguration('mindReader').get('reader.contextWindow')!;
-
- let trimmedText = text.trimStart(); // trim leading whitespace
- let leadingWS = text.length - trimmedText.length; // # of characters of leading whitespace
- trimmedText = trimmedText.trimEnd(); // trim trailing whitespace
- let pos = leadingWS;
- let maxPos = text.length;
-
- // clamp cursor start/end to new range
- let col = cursorPos.character; // effective column of the cursor position
- if (col < leadingWS) {
- // move effective start to first non-whitespace character in the line
- col = leadingWS;
- } else if (col > leadingWS + trimmedText.length - 1) {
- // move effective end to last non-whitespace character in the line
- col = leadingWS + trimmedText.length - 1;
- }
-
- // generate list of space separate words with range data (start, end)
- // TODO: can find user position to be done in one pass
- let spaceWords: {word: string, start: number, end: number}[] = [];
- while (pos < maxPos && trimmedText.length > 0) {
- let word = trimmedText.replace(/ .*/, '');
- spaceWords.push({word, start: pos, end: pos+word.length});
-
- // remove processed word from trimmed text
- const oldText = trimmedText;
- trimmedText = trimmedText.replace(/[^ ]+/, '').trimStart();
-
- // update pos to start of next word
- pos += oldText.length - trimmedText.length;
- }
-
- // find word the user is in
- let contextStart: number = -1, contextEnd: number = -1;
- for (let i = 0; i < spaceWords.length; i++) {
- if (col >= spaceWords[i].start && col <= spaceWords[i].end) {
- // found the word
- contextStart = Math.max(0, i - windowSize); // clamp start index
- contextEnd = Math.min(spaceWords.length, i + windowSize + 1); // clamp end index
-
- // construct cursor context string
- let contextString = '';
- for (let i = contextStart; i < contextEnd; i++) {
- contextString += spaceWords[i].word + ' ';
- }
-
- // output cursor context string
- vscode.window.showInformationMessage(contextString);
-
- return;
+ if (!editor) {
+ window.showErrorMessage('RunCursorContext: No Active Editor');
+ return;
}
- }
-}
+ const cursorPos : Position = editor.selection.active;
+ const text : string = editor.document.lineAt(cursorPos).text;
+ const windowSize : any = workspace.getConfiguration('mind-reader').get('reader.contextWindow');
+ let trimmedText: string = text.trimStart(); // trim leading whitespace
+ const leadingWS : number = text.length - trimmedText.length; // # of characters of leading whitespace
+ let pos : number = leadingWS;
+ const maxPos : number = text.length;
+ // clamp cursor start/end to new range
+ let col : number = cursorPos.character; // effective column of the cursor position
+
+ trimmedText = trimmedText.trimEnd(); // trim trailing whitespace
+
+ if (col < leadingWS) {
+ // move effective start to first non-whitespace character in the line
+ col = leadingWS;
+ }
+ else if (col > leadingWS + trimmedText.length - 1) {
+ // move effective end to last non-whitespace character in the line
+ col = leadingWS + trimmedText.length - 1;
+ }
+
+ // generate list of space separate words with range data (start, end)
+ // TODO: can find user position to be done in one pass
+ const spaceWords: any[] = [];
+
+ while (pos < maxPos && trimmedText.length > 0) {
+ const word: string = trimmedText.replace(/ .*/, '');
+
+ spaceWords.push({
+ word,
+ start: pos,
+ end: pos + word.length
+ });
+
+ // remove processed word from trimmed text
+ const oldText: string = trimmedText;
+ trimmedText = trimmedText.replace(/[^ ]+/, '').trimStart();
+ // update pos to start of next word
+ pos += oldText.length - trimmedText.length;
+ }
+
+ // find word the user is in
+ let contextStart: number = -1;
+ let contextEnd : number = -1;
+
+ for (let i: number = 0; i < spaceWords.length; i++) {
+ if (col >= spaceWords[i].start && col <= spaceWords[i].end) {
+ // found the word
+ contextStart = Math.max(0, i - windowSize); // clamp start index
+ contextEnd = Math.min(spaceWords.length, i + windowSize + 1); // clamp end index
+ // construct cursor context string
+ let contextString: string = '';
+
+ for (let i: number = contextStart; i < contextEnd; i++) {
+ contextString += spaceWords[i].word + ' ';
+ }
+ // output cursor context string
+ window.showInformationMessage(contextString);
+ return;
+ }
+ }
+}
diff --git a/src/ev3Manager.ts b/src/ev3Manager.ts
new file mode 100644
index 0000000..a251476
--- /dev/null
+++ b/src/ev3Manager.ts
@@ -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 | 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 {
+ 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;
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index 5496408..18c2158 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,49 +1,47 @@
-import * as vscode from 'vscode';
-import * as pl from './pylex';
+import * as vscode from "vscode";
+import * as pl from "./pylex";
+import CommandNodeProvider from "./commandNodeProvider";
+import Logger from "./log";
+import { lineHighlighter } from "./lineHighlighter";
-import {
- accessCommands,
- hubCommands,
- navCommands,
- textCommands
-} from './commands';
-
-import CommandNodeProvider from './commandNodeProvider';
-import Logger from './log';
+import { accessCommands, hubCommands, navCommands, textCommands } from "./commands";
// Output Logger
-const product: string = vscode.workspace.getConfiguration('mindReader').get('productType')!;
-const outputChannel = vscode.window.createOutputChannel(product + " Output");
-export const logger = new Logger(outputChannel);
+const product: string = vscode.workspace.getConfiguration("mind-reader").get("productType")!;
+const outputChannel = vscode.window.createOutputChannel(product + " Output");
+export const logger = new Logger(outputChannel);
let parser: pl.Parser = new pl.Parser();
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 = [
accessCommands,
hubCommands,
navCommands,
- textCommands
+ textCommands,
].flat(1);
// Register Commands
- allCommands.forEach(command => {
- let disposable = vscode.commands.registerCommand(
- command.name,
- command.callback
+ allCommands.forEach((command) => {
+ context.subscriptions.push(
+ vscode.commands.registerCommand(command.name, command.callback)
);
- context.subscriptions.push(disposable);
});
- let accessProvider = new CommandNodeProvider([accessCommands, textCommands].flat(1));
- vscode.window.registerTreeDataProvider('accessActions', accessProvider);
+ let accessProvider = new CommandNodeProvider(
+ [accessCommands, textCommands].flat(1)
+ );
+ vscode.window.registerTreeDataProvider("accessActions", accessProvider);
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() {}
diff --git a/src/hubManager.ts b/src/hubManager.ts
index 8d5bf7b..13dda7e 100644
--- a/src/hubManager.ts
+++ b/src/hubManager.ts
@@ -76,12 +76,14 @@ export default class HubManager {
// 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
- 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) {
+ // check if message is actually json
+ if (!msg.includes("{")) { continue; }
// check if this msg is a response to a pending request
try {
let json: { [key: string]: any };
@@ -112,11 +114,15 @@ export default class HubManager {
case 'runtime_error':
logger.error(Buffer.from(params[3], 'base64').toString());
break;
+ case 2:
+ logger.info(`Battery at ${params[0]}V`);
+ break;
}
vscode.window.showErrorMessage("Program Error.");
+ console.log(`Program error: ${msg}`);
}
} catch (err) {
- console.log('Could not parse JSON:', msg);
+ console.log('Could not parse JSON:', msg, err);
}
}
}
diff --git a/src/lineHighlighter.ts b/src/lineHighlighter.ts
new file mode 100644
index 0000000..149c86c
--- /dev/null
+++ b/src/lineHighlighter.ts
@@ -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 -> # | 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();
+ }
+}
diff --git a/src/pylex/lexer.ts b/src/pylex/lexer.ts
index 1f7c336..1f5e3af 100644
--- a/src/pylex/lexer.ts
+++ b/src/pylex/lexer.ts
@@ -1,9 +1,9 @@
-import { LineToken } from '.';
+import { LineToken } from '.';
import { Symbol, EOFTOKEN, TabInfo } from './token';
type Rule = {
- pattern: RegExp,
- type: Symbol,
+ pattern: RegExp,
+ type : Symbol,
};
/**
@@ -11,205 +11,248 @@ type Rule = {
* The first item is a recognition pattern, used to recognize the token
* the second item is the token type
*/
-const rules: Rule[] = [
- {
- pattern: /^\s*def\s+(?[a-zA-Z_][a-zA-Z0-9_]*)\(/,
- type: Symbol.FUNCTION
- },
- {
- pattern: /^\s*class\s+(?[a-zA-Z_][a-zA-Z0-9_]*)/,
- type: Symbol.CLASS
- },
- {
- pattern: /^\s*if\s+(?[^:]+):\s*/,
- type: Symbol.IF
- },
- {
- pattern: /^\s*elif\s+(?[^:]+):\s*$/,
- type: Symbol.ELIF
- },
- {
- pattern: /^\s*else\s*:/,
- type: Symbol.ELSE
- },
- {
- pattern: /^\s*for\s+(?[^:]+):\s*$/,
- type: Symbol.FOR
- },
- {
- pattern: /^\s*while\s+(?[^:]+):\s*$/,
- type: Symbol.WHILE
- },
- {
- pattern: /^\s*try\s*:/,
- type: Symbol.TRY
- },
- {
- pattern: /^\s*except(\s*(?[^:]+))?:\s*$/,
- type: Symbol.EXCEPT
- },
- {
- pattern: /^\s*finally\s*:\s*$/,
- type: Symbol.FINALLY
- },
- {
- pattern: /^\s*with\s+(?[^:]+):\s*$/,
- type: Symbol.WITH
- },
+const rules: Rule[] = [{
+ pattern: /^\s*def\s+(?[a-zA-Z_][a-zA-Z0-9_]*)\(/,
+ type: Symbol.FUNCTION
+ },
+ {
+ pattern: /^\s*class\s+(?[a-zA-Z_][a-zA-Z0-9_]*)/,
+ type: Symbol.CLASS
+ },
+ {
+ pattern: /^\s*if\s+(?[^:]+):\s*/,
+ type: Symbol.IF
+ },
+ {
+ pattern: /^\s*elif\s+(?[^:]+):\s*$/,
+ type: Symbol.ELIF
+ },
+ {
+ pattern: /^\s*else\s*:/,
+ type: Symbol.ELSE
+ },
+ {
+ pattern: /^\s*for\s+(?[^:]+):\s*$/,
+ type: Symbol.FOR
+ },
+ {
+ pattern: /^\s*while\s+(?[^:]+):\s*$/,
+ type: Symbol.WHILE
+ },
+ {
+ pattern: /^\s*try\s*:/,
+ type: Symbol.TRY
+ },
+ {
+ pattern: /^\s*except(\s*(?[^:]+))?:\s*$/,
+ type: Symbol.EXCEPT
+ },
+ {
+ pattern: /^\s*finally\s*:\s*$/,
+ type: Symbol.FINALLY
+ },
+ {
+ pattern: /^\s*with\s+(?[^:]+):\s*$/,
+ type: Symbol.WITH
+ },
+ {
+ pattern: /^\s*#+\s*(?.*)\s*$/,
+ type: Symbol.COMMENT
+ },
+ {
+ pattern: /^\s*$/,
+ type: Symbol.EMPTY
+ },
+ {
+ pattern: /^\s*(?[^#]+)+\s*$/,
+ type: Symbol.STATEMENT
+ }
];
/**
* Line-By-Line Lexer
*/
export default class Lexer {
- private textLines: string[] = []; // array of text lines
- private pos: number = 0;
- private _currToken: LineToken = EOFTOKEN;
+ private textLines : string[] = []; // array of text lines
+ private pos : number = 0;
+ 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!);
- }
+ /**
+ * @param `text` The text to lex.
+ * @param `tabFmt` A tab information descriptor
+ */
+ constructor(text ? : string, private tabFmt ? : TabInfo) {
+ // default is 4 wide expanded tabs
+ this.tabFmt = {
+ ...{
+ size: 4,
+ hard: false
+ },
+ ...tabFmt
+ };
- return indent;
- }
-
- /**
- * @param `text` The text to lex.
- * @param `tabFmt` A tab information descriptor
- */
- constructor(text?: string, private tabFmt?: TabInfo) {
- // default is 4 wide expanded tabs
- this.tabFmt = {
- ...{size: 4, hard: false},
- ...tabFmt
- };
-
- if (text) {
- // normalize linefeeds
- text = text.replace('\r\n', '\n');
- }
- this.restart(text);
- }
-
- /**
- * Restart lexer with new text.
- *
- * @param `text` The new text to lex.
- */
- restart(text?: string): void {
- this.pos = 0;
- this._currToken = EOFTOKEN; // if no input, already on EOFTOKEN
- if (text) {
- this.textLines = text.split('\n');
- this.next(); // advance to the first token
- }
- }
-
- /**
- * @return the current {@link LineToken}.
- */
- currToken(): LineToken { return this._currToken; }
-
- /**
- * Advance the position in the token stream.
- *
- * @return The new current token, after advancing
- */
- next(): LineToken {
- if (this._currToken === EOFTOKEN && this.pos > this.textLines.length) {
- throw new Error('Cannot advance past end');
- }
-
- // Until a LineToken is found, or EOF
- while (this.pos < this.textLines.length) {
- let line: string = this.textLines[this.pos];
- let indent: number = Lexer.getIndent(line, this.tabFmt!);
- let token: LineToken;
- for (var r of rules) {
- // Does line match pattern?
- let match: RegExpMatchArray | null = line.match(r.pattern);
- if (match) {
- // Yes...
- if (match.groups) {
- token = new LineToken(r.type, this.pos, indent, match.groups["attr"]);
- } else {
- token = new LineToken(r.type, this.pos, indent);
- }
-
- this._currToken = token;
- this.pos++;
- return this.currToken();
+ if (text) {
+ // normalize line feeds
+ text = text.replace('\r\n', '\n');
}
- }
- // No rules matched
-
- // 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.pos++;
- return this.currToken();
+ this.restart(text);
}
- // Didn't return, must be EOF
- this._currToken = EOFTOKEN;
- this.pos++;
- return this.currToken();
- }
-
- /**
- * Move backwards in the token stream
- *
- * @param `n` The number of positions to retract.
- * @return The new current token after retracting.
- */
- retract(n: number = 1): LineToken {
- if (this.pos - 1 - n < 0) {
- // -1 because this.pos is currently on the next token
- throw new RangeError('Cannot retract past start');
+ /**
+ * Restart lexer with new text.
+ *
+ * @param `text` The new text to lex.
+ */
+ restart(text ? : string): void {
+ this.pos = 0;
+ this._currToken = EOFTOKEN; // if no input, already on EOFTOKEN
+ if (text) {
+ this.textLines = text.split('\n');
+ this.next(); // advance to the first token
+ }
}
- if (n <= 0) {
- throw new RangeError('Retract distance must be positive');
+ /**
+ * @return the current {@link LineToken}.
+ */
+ currToken(): LineToken {
+ return this._currToken;
}
- if (this.pos - n === 0) {
- // just restart
- this.pos = 0;
- return this.next();
+ /**
+ * Advance the position in the token stream.
+ *
+ * @return The new current token, after advancing
+ */
+ next(): LineToken {
+ if (this._currToken === EOFTOKEN && this.pos > this.textLines.length) {
+ throw new Error('Cannot advance past end');
+ }
+
+ // Until a LineToken is found, or EOF
+ while (this.pos < this.textLines.length) {
+ const line : string = this.textLines[this.pos];
+ const indent: number = Lexer.getIndent(line, this.tabFmt!);
+ let token : LineToken;
+
+ for (var r of rules) {
+ // Does line match pattern?
+ const match: RegExpMatchArray | null = line.match(r.pattern);
+ if (match) {
+ // Yes...
+ if (match.groups) {
+ token = new LineToken(r.type, this.pos, indent, match.groups["attr"]);
+ } else {
+ token = new LineToken(r.type, this.pos, indent);
+ }
+
+ this._currToken = token;
+ this.pos++;
+
+ return this.currToken();
+ }
+ }
+ // No rules matched
+ token = new LineToken(Symbol.INVALID, this.pos, 999999);
+ this._currToken = token;
+ this.pos++;
+
+ return this.currToken();
+ }
+
+ // Didn't return, must be EOF
+ this._currToken = EOFTOKEN;
+ this.pos++;
+
+ return this.currToken();
}
- let c = n + 1;
- while (c > 0) {
- this.pos--;
- while (/^\s*(#.*)?$/.test(this.textLines[this.pos])) {
- // Skip empty lines
- this.pos--;
- }
- c--;
+ /**
+ * Move backwards in the token stream
+ *
+ * @param `n` The number of positions to retract.
+ * @return The new current token after retracting.
+ */
+ retract(n: number = 1): LineToken {
+ if (this.pos - 1 - n < 0) {
+ // -1 because this.pos is currently on the next token
+ throw new RangeError('Cannot retract past start');
+ }
+
+ if (n <= 0) {
+ throw new RangeError('Retract distance must be positive');
+ }
+
+ if (this.pos - n === 0) {
+ // just restart
+ this.pos = 0;
+ return this.next();
+ }
+
+ let c = n + 1;
+ while (c > 0) {
+ this.pos--;
+ while (/^\s*(#.*)?$/.test(this.textLines[this.pos])) {
+ // Skip empty lines
+ this.pos--;
+ }
+ c--;
+ }
+
+ 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;
}
- return this.next();
- }
}
diff --git a/src/pylex/parser.ts b/src/pylex/parser.ts
index 9a2fb18..d6771a8 100644
--- a/src/pylex/parser.ts
+++ b/src/pylex/parser.ts
@@ -74,8 +74,9 @@ export default class Parser {
return children;
}
- if (this.lexer.currToken().type === Symbol.INDENT ||
- this.lexer.currToken().type === Symbol.EMPTY) {
+ if (this.lexer.currToken().type === Symbol.STATEMENT ||
+ this.lexer.currToken().type === Symbol.EMPTY ||
+ this.lexer.currToken().type === Symbol.INVALID) {
const label = this.lexer.currToken().type;
// regular code, advance and stay in same block
children.push(new LexNode(
diff --git a/src/pylex/token.ts b/src/pylex/token.ts
index b6a54e0..dcd7e18 100644
--- a/src/pylex/token.ts
+++ b/src/pylex/token.ts
@@ -16,8 +16,10 @@ export enum Symbol {
EXCEPT = "except",
FINALLY = "finally",
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
+ INVALID = "INVALID",
EOF = "EOF"
}
@@ -53,7 +55,7 @@ export default class LineToken {
* @return A string representation of the token
*/
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}`;
}
}
diff --git a/src/test/suites/pylex/lexer.test.ts b/src/test/suites/pylex/lexer.test.ts
index 14bebd0..e863a19 100644
--- a/src/test/suites/pylex/lexer.test.ts
+++ b/src/test/suites/pylex/lexer.test.ts
@@ -12,7 +12,7 @@ suite('Lexer Test Suite', () => {
});
test('Empty String', () => {
- let l: Lexer = new Lexer(undefined);
+ let l: Lexer = new Lexer("");
assert.deepStrictEqual(l.currToken(), EOFTOKEN);
});
@@ -22,18 +22,19 @@ suite('Lexer Test Suite', () => {
});
test('Whitespace', () => {
- let l: Lexer = new Lexer(' \t\t'.repeat(4).repeat(4));
- assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, 999999));
+ let s: string = ' '.repeat(4).repeat(4);
+ let l: Lexer = new Lexer(s);
+ assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, s.length/4));
});
test('Comment', () => {
- let l: Lexer = new Lexer('# ur mom eats toes');
- assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.EMPTY, 0, 999999));
+ let l: Lexer = new Lexer('# this is a comment');
+ assert.deepStrictEqual(l.currToken(), new LineToken(Symbol.COMMENT, 0, 0, 'this is a comment'));
});
test('Non-Whitespace with no construct', () => {
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', () => {
@@ -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 j = 1; j <= 3; j++) {
- let l: Lexer = new Lexer(' '.repeat(i) + ' '.repeat(j) + 'foobar', {size: 4, hard: false});
- assert.strictEqual(l.currToken().indentLevel, i+1);
+ let l: Lexer = new Lexer(' '.repeat(i) + ' '.repeat(j) + 'foobar', {size: size, hard: false});
+ // 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', () => {
let lines: string[] = Array.from(Array(100), (_, i) => 'line' + 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++) {
@@ -169,7 +173,7 @@ suite('Lexer Test Suite', () => {
test('2 full lex and retract passes', () => {
let lines: string[] = Array.from(Array(100), (_, i)=> 'line' + 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'));
diff --git a/src/test/suites/pylex/node.test.ts b/src/test/suites/pylex/node.test.ts
index 85e7fec..381cfd1 100644
--- a/src/test/suites/pylex/node.test.ts
+++ b/src/test/suites/pylex/node.test.ts
@@ -13,7 +13,7 @@ suite('LexNode Test Suite', () => {
});
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);
});
diff --git a/src/test/suites/pylex/parser.test.ts b/src/test/suites/pylex/parser.test.ts
index b92a5ae..5793d77 100644
--- a/src/test/suites/pylex/parser.test.ts
+++ b/src/test/suites/pylex/parser.test.ts
@@ -7,7 +7,7 @@ import LexNode from '../../../pylex/node';
import LineToken from '../../../pylex/token';
import { Symbol } from '../../../pylex/token';
-import { root,indent,empty } from '../../util';
+import { root, statement } from '../../util';
/**
* Test Descriptor
@@ -23,23 +23,24 @@ const tests: ParserTest[] = [
{ name: 'Single Empty Line', input: [''], output: root(null) },
{
name: 'Single Whitespace Only Line',
- input: [' '],
+ input: [' '], // length 20
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',
- input: ['# ur mom likes peas'],
+ input: ['# this is a comment'],
output: root([
- empty(0)
+ new LexNode(Symbol.EMPTY, 0, new LineToken(Symbol.COMMENT, 0, 0, "this is a comment"))
])
},
{
name: 'Single Non-Control Line',
input: ['my_age = 42'],
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 <:)"',
'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!"'
],
output: root([
- new LexNode('if radioshack',
- vscode.TreeItemCollapsibleState.None,
+ new LexNode('if radioshack', 0,
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'
],
output: root([
- indent(0, 0),
- new LexNode('if radioshack',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 1, 0, 'radioshack'),
- [indent(2, 1)])
+ new LexNode('billy = "Scrubbly Bubbles!"', 0, new LineToken(Symbol.STATEMENT, 0, 0, 'billy = "Scrubbly Bubbles!"')),
+ new LexNode('if radioshack', 0, new LineToken(Symbol.IF, 1, 0, 'radioshack'),
+ [
+ new LexNode('print radioshack.hours', 0, new LineToken(Symbol.STATEMENT, 2, 1, 'print radioshack.hours'))
+ ]
+ )
])
},
@@ -101,15 +108,18 @@ const tests: ParserTest[] = [
' print("You really eat this?")',
],
output: root([
- new LexNode('if yummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 0, 0, 'yummy'), [indent(1, 1)]),
- new LexNode('elif just_ok',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.ELIF, 2, 0, 'just_ok'), [indent(3, 1)]),
- new LexNode('else',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.ELSE, 4, 0), [indent(5,1)]),
+ new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
+ [
+ statement(1, 1, 'print("HOoray!")')
+ ]),
+ new LexNode('elif just_ok', 0, new LineToken(Symbol.ELIF, 2, 0, 'just_ok'),
+ [
+ statement(3, 1, 'print("Do you have anything else?")')
+ ]),
+ 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([
new LexNode('if yummy',
- vscode.TreeItemCollapsibleState.None,
+ 0,
new LineToken(Symbol.IF, 0, 0, 'yummy'),
[
- new LexNode('if in_my_tummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
- [indent(2, 2)])
+ new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
+ [
+ statement(2, 2, 'exclaim("Scrumdiddlyumptious!")')
+ ]
+ )
],
)
])
@@ -141,23 +152,20 @@ const tests: ParserTest[] = [
' if in_my_tummy:',
' exclaim("Scrumdiddlyumptious!")',
'else:',
- ' exclaim("DAESGUSTEN~)"'
+ ' exclaim("DISGUSTING!")'
],
output: root([
- new LexNode('if yummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 0, 0, 'yummy'),
+ new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
[
- new LexNode('if in_my_tummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
- [indent(2, 2)])
+ new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
+ [
+ statement(2, 2, 'exclaim("Scrumdiddlyumptious!")')
+ ]
+ )
]
),
- new LexNode('else',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.ELSE, 3, 0),
- [indent(4, 1)]
+ new LexNode('else', 0, new LineToken(Symbol.ELSE, 3, 0),
+ [statement(4, 1, 'exclaim("DISGUSTING!")')]
)
])
},
@@ -168,31 +176,27 @@ const tests: ParserTest[] = [
'if yummy:',
' if in_my_tummy:',
' if looks_like_a_mummy:',
- ' print("you have a spot on your tummy"',
+ ' print("you have a spot on your tummy")',
'else:',
' print("Food is food...")'
],
output: root([
new LexNode('if yummy',
- vscode.TreeItemCollapsibleState.None,
+ 0,
new LineToken(Symbol.IF, 0, 0, 'yummy'),
[
- new LexNode('if in_my_tummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
+ new LexNode('if in_my_tummy', 0, new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
[
- new LexNode('if looks_like_a_mummy',
- vscode.TreeItemCollapsibleState.None,
- new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'),
- [indent(3, 3)])
+ new LexNode('if looks_like_a_mummy', 0, new LineToken(Symbol.IF, 2, 2, 'looks_like_a_mummy'),
+ [statement(3, 3, 'print("you have a spot on your tummy")')])
]
)
]
),
- new LexNode('else',
- vscode.TreeItemCollapsibleState.None,
+ new LexNode('else',
+ 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 in_my_tummy:',
' if looks_like_a_mummy:',
- ' print("you have a spot on your tummy"',
+ ' print("you have a spot on your tummy")',
' else:',
- ' print("eek! a zombie!)',
+ ' print("eek! a zombie!")',
' elif in_my_mouth:',
- ' print("itll be in my tummy soon!"',
+ ' print("itll be in my tummy soon!")',
'else:',
' print("Food is food...")'
],
output: root([
- new LexNode('if yummy',
- vscode.TreeItemCollapsibleState.None,
+ new LexNode('if yummy', 0,
new LineToken(Symbol.IF, 0, 0, 'yummy'),
[
- new LexNode('if in_my_tummy',
- vscode.TreeItemCollapsibleState.None,
+ new LexNode('if in_my_tummy', 0,
new LineToken(Symbol.IF, 1, 1, 'in_my_tummy'),
[
new LexNode('if looks_like_a_mummy',
- vscode.TreeItemCollapsibleState.None,
+ 0,
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',
- vscode.TreeItemCollapsibleState.None,
+ 0,
new LineToken(Symbol.ELSE, 4, 2),
- [indent(5, 3)])
+ [statement(5, 3, 'print("eek! a zombie!")')])
]
),
new LexNode('elif in_my_mouth',
- vscode.TreeItemCollapsibleState.None,
+ 0,
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',
- vscode.TreeItemCollapsibleState.None,
+ new LexNode('else',
+ 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',
input: [
'if yummy:',
- ' print("you have a spot on your tummy"',
+ ' print("you have a spot on your tummy")',
' print("eek! a zombie!)',
- ' print("itll be in my tummy soon!"',
+ ' print("itll be in my tummy soon!")',
'else:',
' print("Food is food...")'
],
output: root([
new LexNode('if yummy', 0, new LineToken(Symbol.IF, 0, 0, 'yummy'),
[
- indent(1, 1),
- indent(2, 1),
- indent(3, 1),
+ statement(1, 1, 'print("you have a spot on your tummy")'),
+ statement(2, 1, 'print("eek! a zombie!)'),
+ 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...")')
+ ])
])
}
];
diff --git a/src/test/util.ts b/src/test/util.ts
index b7c0f12..0e392d2 100644
--- a/src/test/util.ts
+++ b/src/test/util.ts
@@ -50,19 +50,19 @@ function root(nodes: LexNode[] | null): LexNode {
}
/* short hand for returning an indentation token for a certain line and indentation */
-function indent(linenr: number, indentLevel: number): LexNode {
- return new LexNode('INDENT', 0, new LineToken(PylexSymbol.INDENT, linenr, indentLevel));
+function statement(linenr: number, indentLevel: number, text: string = ""): LexNode {
+ return new LexNode(PylexSymbol.STATEMENT, 0, new LineToken(PylexSymbol.STATEMENT, linenr, indentLevel, text));
}
/* short hand for returning an empty token for a certain line*/
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 {
deparent,
root,
- indent,
+ statement as statement,
empty
};