mirror of
				https://github.com/We-Dont-Byte/Mind_Reader.git
				synced 2025-02-04 10:38:42 +00:00 
			
		
		
		
	Merge branch 'master' of https://github.com/SingleSemesterSnobs/Mind_Reader into sophiabranch
This commit is contained in:
		
							
								
								
									
										72
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								README.md
									
									
									
									
									
								
							@@ -1,6 +1,6 @@
 | 
			
		||||
<!-- header with logo -->
 | 
			
		||||
<p align="center">
 | 
			
		||||
<img alt="Mind Reader Logo" src="./media/logo.png"></img>
 | 
			
		||||
<img alt="Mind Reader Logo" src="media/logo.png"></img>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
<h1>Mind_Reader</h1>
 | 
			
		||||
@@ -27,21 +27,77 @@ Python programming with LEGO Mindstorms. Our goal is to:
 | 
			
		||||
    - [JAWS](https://www.freedomscientific.com/products/software/jaws/)
 | 
			
		||||
    - [Apple VoiceOver](https://support.apple.com/guide/voiceover-guide/welcome/web/)
 | 
			
		||||
 | 
			
		||||
<!-- TODO: still need this -->
 | 
			
		||||
- Play audio alerts for syntax and runtime errors.
 | 
			
		||||
 | 
			
		||||
- Present a summary of the scope for an individual line of code.
 | 
			
		||||
 | 
			
		||||
- Save and load programs directly onto the LEGO Hub from within Visual Studio Code
 | 
			
		||||
 | 
			
		||||
# For Developers
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
<!-- TODO: version information -->
 | 
			
		||||
<!-- TODO: how to support native-usb functionality? -->
 | 
			
		||||
- [Git](https://git-scm.com/)
 | 
			
		||||
- [Node.js](https://nodejs.org/en/)
 | 
			
		||||
 | 
			
		||||
**NOTE**: While installing Node.js, there will be a section titled "Tools for Native Modules". Make sure that
 | 
			
		||||
'Automatically install the necessary tools' is checked:
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
<img width="50%" height="50%" alt="tools for native modules page with tool installation checked" src="media/nodejs_setup.png"></img>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
If the compiled serial port version is incompatible, you may see no options presented in the Mind_Reader actions panel:
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
<img width="50%" height="50%" alt="mind reader actions panel with no items:" src="media/missing_actions.png"></img>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
In this case, you will also need to rebuild the serial port component with `electron-rebuild`. This is a one-time setup
 | 
			
		||||
for each version of Visual Studio Code. You may need to repeat this process if you update your version of Visual Studio
 | 
			
		||||
Code.
 | 
			
		||||
 | 
			
		||||
## Installing `electron-rebuild`
 | 
			
		||||
**Use Git Bash on Windows, and the terminal on MacOS/Linux. These steps will refer to this as 'the terminal'**
 | 
			
		||||
 | 
			
		||||
### 1 Install the `electron-rebuild` tool
 | 
			
		||||
In the terminal install electron rebuild with `npm` that is included with [Node.js](https://nodejs.org/en/):
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ npm install -g electron-rebuild
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 2 Finding your electron version
 | 
			
		||||
On MacOS, go to Code > About Visual Studio Code.
 | 
			
		||||
 | 
			
		||||
On Windows and Linux, go to Help > About.
 | 
			
		||||
 | 
			
		||||
The electron version should be listed, e.g.: `Electron: 13.5.2`
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
<img width="35%" height="35%" alt="vscode information" src="media/vscode_info.png"></img>
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
### 3 Finding the Mind_Reader extension directory
 | 
			
		||||
On MacOS and Linux this is `~/.vscode/extensions`.
 | 
			
		||||
 | 
			
		||||
On Windows this is `C:\<YOUR USER>\.vscode\extensions\`. However, in Git Bash, it will appear like on MacOS and Linux
 | 
			
		||||
e.g.: `~/.vscode/extensions`.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Find the Mind_Reader extension folder, this should look like `xxx.mind-reader-x.x.x`.
 | 
			
		||||
 | 
			
		||||
Navigate to the found folder in the terminal.
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ cd ~/.vscode/extensions/<mind_reader_folder>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 4 Running `electron-rebuild`
 | 
			
		||||
 | 
			
		||||
Then, run `electron-rebuild` with `ELECTRON_VERSION` replaced with the electron version found in step 2:
 | 
			
		||||
 | 
			
		||||
```console
 | 
			
		||||
$ electron-rebuild --version=ELECTRON_VERSION
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# For Developers
 | 
			
		||||
## Development Quick Start
 | 
			
		||||
Use the following to set up the extension for development.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								media/html/mackeys.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								media/html/mackeys.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Mind Reader Key Bindings for Mac</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
<h2> Here is a list of all the Mind Reader commands and their keybindings on Mac systems </h2>
 | 
			
		||||
<p>
 | 
			
		||||
    <h2> Editor Settings </h2>
 | 
			
		||||
    Increase Font Scale - <br>
 | 
			
		||||
    Decrease Font Scale - <br>
 | 
			
		||||
    Increase Editor Scale - <br>
 | 
			
		||||
    Decrease Editor Scale - <br>
 | 
			
		||||
    Reset Editor Scale - <br>
 | 
			
		||||
    Select Theme - <br>
 | 
			
		||||
    <h2>Navigation</h2>
 | 
			
		||||
    Get Indent - <br>
 | 
			
		||||
    Show All Symbols - <br>
 | 
			
		||||
    Go To Line - <br>
 | 
			
		||||
    Quick Open - <br>
 | 
			
		||||
    Go To Symbol - <br>
 | 
			
		||||
    Show Problems - <br>
 | 
			
		||||
    Next In File - <br>
 | 
			
		||||
    Previous In File - <br>
 | 
			
		||||
    Open Previous Editor Group - <br>
 | 
			
		||||
    Navigate Forward - <br>
 | 
			
		||||
    Navigate Back - <br>
 | 
			
		||||
    Get Quick Input Back - 
 | 
			
		||||
</p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										43
									
								
								media/html/main.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								media/html/main.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Mind Reader</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT6a4XaqHkKcxJ6ZFms1RNrRurcOfl-diW90DAdpAx0Kv-rtrLJXovIhcUpayqFHATkrQ&usqp=CAU" width="600" />
 | 
			
		||||
    <p></p>
 | 
			
		||||
    <h1>Welcome to Mind_Reader!</h1>
 | 
			
		||||
    <p>We are the Single Semester Snobs and this is our tool to Help Blind Students Program Lego Mindstorms Robots in Python.</p>
 | 
			
		||||
    <ul>
 | 
			
		||||
      <li>
 | 
			
		||||
        This tool includes features such as a hotkey that says how many spaces in the text starts, an Accessibility Pane,
 | 
			
		||||
        Audio Alerts, and an advanced settings window.
 | 
			
		||||
        <br>
 | 
			
		||||
        The tool has hotkeys for both PC and Mac commands.
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>This system is intended for everyone, but primarily for students K-12 who are visually impaired. </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        Our goal is to provide an enhanced experience for students who are visually impaired that is transparent to 
 | 
			
		||||
        sighted students.
 | 
			
		||||
        <br>
 | 
			
		||||
        This allows for everyone to use the same software solution, whether or not they are 
 | 
			
		||||
        vision impaired.
 | 
			
		||||
      </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    <p>Use the following key binding to bring up a page for all key bindings for windows
 | 
			
		||||
    <br>
 | 
			
		||||
    Control and Shift and 8
 | 
			
		||||
    </p>
 | 
			
		||||
    <p>Use this key binding to do the same for mac computers:
 | 
			
		||||
    <br>
 | 
			
		||||
    Command and Shift and 9
 | 
			
		||||
    </p>
 | 
			
		||||
    <h2>This is the Lego Spike Prime!</h2z>
 | 
			
		||||
    <p></p>
 | 
			
		||||
    <img src="https://cdn.vox-cdn.com/thumbor/qoaa6N2ppl7oj97MR-aj43qPy0w=/0x0:1024x576/920x613/filters:focal(431x207:593x369):format(webp)/cdn.vox-cdn.com/uploads/chorus_image/image/63339099/lego_spike.0.png" width="300" />
 | 
			
		||||
    <p></p>
 | 
			
		||||
    <a href="https://www.lego.com/en-us/product/lego-education-spike-prime-set-45678">Get the robot!</a>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										33
									
								
								media/html/winkeys.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								media/html/winkeys.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Mind Reader Key Bindings for Windows</title>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
<h2> Here is a list of all Mind Reader's commands and their keybindings on windows and linux systems </h2>
 | 
			
		||||
<p>
 | 
			
		||||
    <h2> Editor Settings </h2>
 | 
			
		||||
    Increase Font Scale - Number Pad Add<br>
 | 
			
		||||
    Decrease Font Scale - Number Pad Subtract<br>
 | 
			
		||||
    Increase Editor Scale - Shift and Number Pad Add<br>
 | 
			
		||||
    Decrease Editor Scale - Shift and Number Pad Subtract<br>
 | 
			
		||||
    Reset Editor Scale - Shift and Enter<br>
 | 
			
		||||
    Select Theme - Control and Shift and 1<br>
 | 
			
		||||
    <h2>Navigation</h2>
 | 
			
		||||
    Get Indent - Shift and Tab<br>
 | 
			
		||||
    Show All Symbols - Control and T<br>
 | 
			
		||||
    Go To Line - Control and G<br>
 | 
			
		||||
    Quick Open - Control and P<br>
 | 
			
		||||
    Go To Symbol - Control and Shift and 0<br>
 | 
			
		||||
    Show Problems - Control and Shift and M<br>
 | 
			
		||||
    Next In File - F8<br>
 | 
			
		||||
    Previous In File - Shift and F8<br>
 | 
			
		||||
    Open Previous Editor Group - Control and Tab<br>
 | 
			
		||||
    Navigate Forward - Control and Shift and Minus<br>
 | 
			
		||||
    Navigate Back - Control and Alt and Minus<br>
 | 
			
		||||
    Get Quick Input Back - Control and Alt and Minus
 | 
			
		||||
</p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								media/missing_actions.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								media/missing_actions.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 91 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								media/nodejs_setup.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								media/nodejs_setup.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 20 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								media/vscode_info.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								media/vscode_info.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 99 KiB  | 
							
								
								
									
										1214
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1214
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										249
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										249
									
								
								package.json
									
									
									
									
									
								
							@@ -1,6 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "mind-reader",
 | 
			
		||||
	"displayName": "Mind_Reader",
 | 
			
		||||
	"repository": "https://github.com/SingleSemesterSnobs/Mind_Reader",
 | 
			
		||||
	"description": "",
 | 
			
		||||
	"version": "0.0.1",
 | 
			
		||||
	"engines": {
 | 
			
		||||
@@ -47,12 +48,22 @@
 | 
			
		||||
				"command": "mind-reader.selectTheme",
 | 
			
		||||
				"title": "Select Theme"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.openWebview",
 | 
			
		||||
				"title": "Mind Reader Webview"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.openKeyBindWin",
 | 
			
		||||
				"title": "Key Bindings for Windows"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.openKeyBindMac",
 | 
			
		||||
				"title": "Key Bindings for Mac"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.runLineContext",
 | 
			
		||||
				"title": "Run Line Context"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.runCursorContext",
 | 
			
		||||
				"title": "Run Cursor Context"
 | 
			
		||||
@@ -60,117 +71,134 @@
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.getIndent",
 | 
			
		||||
				"title": "Get Line Indentation"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.connectHub",
 | 
			
		||||
				"title": "Connect LEGO Hub"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.disconnectHub",
 | 
			
		||||
				"title": "Disconnect LEGO Hub"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.uploadCurrentFile",
 | 
			
		||||
				"title": "Upload current file to LEGO Hub"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.runProgram",
 | 
			
		||||
				"title": "Run a program from the LEGO Hub"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.stopExecution",
 | 
			
		||||
				"title": "Stop running program on the LEGO Hub"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.deleteProgram",
 | 
			
		||||
				"title": "Delete a program from the LEGO Hub"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		],
 | 
			
		||||
        "keybindings": [
 | 
			
		||||
            {
 | 
			
		||||
              "command": "mind-reader.decreaseFontScale",
 | 
			
		||||
              "key": "numpad_subtract",
 | 
			
		||||
              "mac": ""
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                "command": "mind-reader.increaseFontScale",
 | 
			
		||||
                "key": "numpad_add",
 | 
			
		||||
                "mac": ""
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                "command": "mind-reader.increaseEditorScale",
 | 
			
		||||
                "key": "shift+numpad_add"
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                "command": "mind-reader.decreaseEditorScale",
 | 
			
		||||
                "key": "shift+numpad_subtract",
 | 
			
		||||
                "mac": ""
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
                "command": "mind-reader.resetEditorScale",
 | 
			
		||||
                "key": "shift+enter",
 | 
			
		||||
                "mac": ""
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
		"keybindings": [
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.decreaseFontScale",
 | 
			
		||||
				"key": "numpad_subtract",
 | 
			
		||||
				"mac": "d"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.increaseFontScale",
 | 
			
		||||
				"key": "numpad_add",
 | 
			
		||||
				"mac": "[NumpadAdd]"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"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": ""
 | 
			
		||||
				"mac": "Cmd+[KeyT]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.gotoLine",
 | 
			
		||||
				"key": "CTRL+G",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+[KeyG]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.quickOpen",
 | 
			
		||||
				"key": "CTRL+P",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
			},			
 | 
			
		||||
 | 
			
		||||
				"mac": "Cmd+[KeyP]"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.gotoSymbol",
 | 
			
		||||
				"key": "Ctrl+Shift+O",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Shift+[KeyO]"
 | 
			
		||||
			},
 | 
			
		||||
			
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.showProblems",
 | 
			
		||||
				"key": "Ctrl+Shift+M",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Shift+[KeyM]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.nextInFiles",
 | 
			
		||||
				"key": "F8",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "[F8]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.prevInFiles",
 | 
			
		||||
				"key": "Shift+F8",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Shift+[F8]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.quickOpenPreviousRecentlyUsedEditorInGroup",
 | 
			
		||||
				"key": "Ctrl+Tab",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+[Tab]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.navigateBack",
 | 
			
		||||
				"key": "Ctrl+Alt+-",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Alt+[Minus]"
 | 
			
		||||
			},
 | 
			
		||||
		
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.getuickInputBack",
 | 
			
		||||
				"command": "mind-reader.getQuickInputBack",
 | 
			
		||||
				"key": "Ctrl+Alt+-",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Alt+[Minus]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.navigateForward",
 | 
			
		||||
				"key": "Ctrl+Shift+-",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Shift+[Minus]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.selectTheme",
 | 
			
		||||
				"key": "Ctrl+Shift+1",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Cmd+Shift+[Digit1]"
 | 
			
		||||
			},
 | 
			
		||||
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.getIndent",
 | 
			
		||||
				"key": "Shift+Tab",
 | 
			
		||||
				"mac": ""
 | 
			
		||||
				"mac": "Shift+[Tab]"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.openKeyBindWin",
 | 
			
		||||
				"key": "Ctrl+Shift+8",
 | 
			
		||||
				"mac": "Cmd+Shift+8"
 | 
			
		||||
			},
 | 
			
		||||
			{
 | 
			
		||||
				"command": "mind-reader.openKeyBindMac",
 | 
			
		||||
				"key": "Ctrl+Shift+9",
 | 
			
		||||
				"mac": "Cmd+Shift+9"
 | 
			
		||||
			}
 | 
			
		||||
        ],
 | 
			
		||||
		],
 | 
			
		||||
		"menus": {
 | 
			
		||||
			"editor/context": [
 | 
			
		||||
				{
 | 
			
		||||
@@ -213,6 +241,21 @@
 | 
			
		||||
					"command": "mind-reader.selectTheme",
 | 
			
		||||
					"group": "mind-reader",
 | 
			
		||||
					"when": "activeEditor"
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"command": "mind-reader.openWebview",
 | 
			
		||||
					"group": "mind-reader",
 | 
			
		||||
					"when": "activeEditor"
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"command": "mind-reader.openKeyBindWin",
 | 
			
		||||
					"group": "mind-reader",
 | 
			
		||||
					"when": "activeEditor"
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"command": "mind-reader.openKeyBindMac",
 | 
			
		||||
					"group": "mind-reader",
 | 
			
		||||
					"when": "activeEditor"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
@@ -225,20 +268,20 @@
 | 
			
		||||
		"configuration": {
 | 
			
		||||
			"title": "Mind_Reader",
 | 
			
		||||
			"properties": {
 | 
			
		||||
				"mindreader.productType": {
 | 
			
		||||
				"mindReader.productType": {
 | 
			
		||||
					"type": "string",
 | 
			
		||||
					"description": "Specifies the LEGO® product.",
 | 
			
		||||
					"default": "MINDSTORMS® EV3",
 | 
			
		||||
					"default": "MINDSTORMS EV3",
 | 
			
		||||
					"enum": [
 | 
			
		||||
						"MINDSTORMS® EV3",
 | 
			
		||||
						"SPIKE™ Prime"
 | 
			
		||||
						"MINDSTORMS EV3",
 | 
			
		||||
						"SPIKE Prime"
 | 
			
		||||
					],
 | 
			
		||||
					"enumDescriptions": [
 | 
			
		||||
						"LEGO® MINDSTORMS® EV3 (31313)",
 | 
			
		||||
						"LEGO® Education SPIKE™ Prime Set (45678)"
 | 
			
		||||
					]
 | 
			
		||||
				},
 | 
			
		||||
				"mindreader.reader.screenReader": {
 | 
			
		||||
				"mindReader.reader.screenReader": {
 | 
			
		||||
					"type": "string",
 | 
			
		||||
					"description": "Specifies which screen reader to optimize for.",
 | 
			
		||||
					"default": "NVDA",
 | 
			
		||||
@@ -253,41 +296,47 @@
 | 
			
		||||
						"Apple VoiceOver (macOS)"
 | 
			
		||||
					]
 | 
			
		||||
				},
 | 
			
		||||
				"mindreader.reader.contextWindow": {
 | 
			
		||||
				"mindReader.reader.contextWindow": {
 | 
			
		||||
					"type": "number",
 | 
			
		||||
					"description": "The number of words around the cursor to use when reading the cursor context",
 | 
			
		||||
					"default": 1
 | 
			
		||||
				},
 | 
			
		||||
				"mindreader.connection.connectAutomatically": {
 | 
			
		||||
					"type": "boolean",
 | 
			
		||||
					"description": "Specifies whether to try to automatically detect and communicate with a connected Hub.",
 | 
			
		||||
					"default": "true"
 | 
			
		||||
				},
 | 
			
		||||
				"mindreader.connection.portPath": {
 | 
			
		||||
				"mindReader.connection.portPath": {
 | 
			
		||||
					"type": "string",
 | 
			
		||||
					"markdownDescription": "Specifies the serial port path to use if `#mindreader.connectAutomatically#` is not set."
 | 
			
		||||
					"markdownDescription": "Specifies the serial port path to use if `#mindReader.connectAutomatically#` is not set."
 | 
			
		||||
				},
 | 
			
		||||
				"mindReader.connection.clearOutputOnRun": {
 | 
			
		||||
					"type": "boolean",
 | 
			
		||||
					"description": "Whether to clear the output each time the program is run",
 | 
			
		||||
					"default": "true"
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
    "views": {
 | 
			
		||||
      "accessActions": [
 | 
			
		||||
        {
 | 
			
		||||
          "id": "accessActions",
 | 
			
		||||
          "name": "Access Actions",
 | 
			
		||||
          "icon": "media/dep.svg",
 | 
			
		||||
          "contextualTitle": "Accessibility Menu Actions"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    },
 | 
			
		||||
    "viewsContainers": {
 | 
			
		||||
      "activitybar": [
 | 
			
		||||
        {
 | 
			
		||||
          "id": "accessActions",
 | 
			
		||||
          "title": "Access Actions",
 | 
			
		||||
          "icon": "media/dep.svg"
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
		"views": {
 | 
			
		||||
			"MindReader": [
 | 
			
		||||
				{
 | 
			
		||||
					"id": "accessActions",
 | 
			
		||||
					"name": "Access Actions",
 | 
			
		||||
					"icon": "media/dep.svg",
 | 
			
		||||
					"contextualTitle": "Accessibility Menu Actions"
 | 
			
		||||
				},
 | 
			
		||||
				{
 | 
			
		||||
					"id": "hubActions",
 | 
			
		||||
					"name": "Hub Actions",
 | 
			
		||||
					"icon": "media/dep.svg",
 | 
			
		||||
					"contextualTitle": "Hub Connection Actions"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		"viewsContainers": {
 | 
			
		||||
			"activitybar": [
 | 
			
		||||
				{
 | 
			
		||||
					"id": "MindReader",
 | 
			
		||||
					"title": "MindReader Actions",
 | 
			
		||||
					"icon": "media/dep.svg"
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"vscode:prepublish": "npm run compile",
 | 
			
		||||
@@ -298,17 +347,21 @@
 | 
			
		||||
		"test": "node ./out/test/runTest.js"
 | 
			
		||||
	},
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"@types/vscode": "^1.60.0",
 | 
			
		||||
		"@types/glob": "^7.1.3",
 | 
			
		||||
		"@types/mocha": "^8.2.2",
 | 
			
		||||
		"@types/node": "14.x",
 | 
			
		||||
		"eslint": "^7.27.0",
 | 
			
		||||
		"@types/node": "16.x",
 | 
			
		||||
		"@types/serialport": "^8.0.2",
 | 
			
		||||
		"@types/vscode": "^1.60.0",
 | 
			
		||||
		"@typescript-eslint/eslint-plugin": "^4.26.0",
 | 
			
		||||
		"@typescript-eslint/parser": "^4.26.0",
 | 
			
		||||
		"@vscode/test-electron": "^1.6.2",
 | 
			
		||||
		"eslint": "^7.27.0",
 | 
			
		||||
		"glob": "^7.1.7",
 | 
			
		||||
		"mocha": "^8.4.0",
 | 
			
		||||
		"typescript": "^4.3.2",
 | 
			
		||||
		"vscode-test": "^1.5.2",
 | 
			
		||||
		"@vscode/test-electron": "^1.6.2"
 | 
			
		||||
		"vscode-test": "^1.5.2"
 | 
			
		||||
	},
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"serialport": "^9.2.5"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								src/commandNodeProvider.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/commandNodeProvider.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
import * as vscode from 'vscode';
 | 
			
		||||
 | 
			
		||||
import { CommandEntry } from './commands';
 | 
			
		||||
 | 
			
		||||
export class CommandItem extends vscode.TreeItem {
 | 
			
		||||
  constructor(
 | 
			
		||||
    public readonly label: string,
 | 
			
		||||
    public readonly command: vscode.Command
 | 
			
		||||
  ) {
 | 
			
		||||
    super(label, vscode.TreeItemCollapsibleState.None);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default class CommandNodeProvider implements vscode.TreeDataProvider<CommandItem> {
 | 
			
		||||
  private items: CommandItem[] = [];
 | 
			
		||||
 | 
			
		||||
  public constructor(commands: CommandEntry[]) {
 | 
			
		||||
    // build and cache command items
 | 
			
		||||
    for (const c of commands) {
 | 
			
		||||
      let humanReadable = c.name.replace(/^mind-reader\./, ''); // strip extensions name
 | 
			
		||||
      // Convert camelCaseText to Title Case Text
 | 
			
		||||
      humanReadable = humanReadable.replace(/([A-Z])/g, ' $1');
 | 
			
		||||
      humanReadable = humanReadable.charAt(0).toUpperCase() + humanReadable.slice(1);
 | 
			
		||||
 | 
			
		||||
      this.items.push(new CommandItem(
 | 
			
		||||
        humanReadable,
 | 
			
		||||
        {
 | 
			
		||||
          title: humanReadable,
 | 
			
		||||
          command: c.name,
 | 
			
		||||
          tooltip: humanReadable
 | 
			
		||||
        }
 | 
			
		||||
      ));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getTreeItem(item: CommandItem): vscode.TreeItem {
 | 
			
		||||
    return item;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async getChildren(): Promise<CommandItem[]> {
 | 
			
		||||
    return Promise.resolve(this.items);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										253
									
								
								src/commands.ts
									
									
									
									
									
								
							
							
						
						
									
										253
									
								
								src/commands.ts
									
									
									
									
									
								
							@@ -1,18 +1,22 @@
 | 
			
		||||
import * as vscode from 'vscode';
 | 
			
		||||
import * as pl from './pylex';
 | 
			
		||||
import * as path from 'path';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
 | 
			
		||||
import HubManager from './hubManager';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @type {Object} Command // Command to register with the VS Code Extension API
 | 
			
		||||
 * @prop {string} command // Name of the command; e.g., 'mind-reader.selectTheme'
 | 
			
		||||
 * @prop {callback} callback // Callback to register when `command` is invoked
 | 
			
		||||
 */
 | 
			
		||||
type Command = {
 | 
			
		||||
export type CommandEntry = {
 | 
			
		||||
  name: string,
 | 
			
		||||
  callback: () => void
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// The list of commands to register in the extension
 | 
			
		||||
const commands: Command[] = [
 | 
			
		||||
// Accessibility Commands
 | 
			
		||||
export const accessCommands: CommandEntry[] = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.selectTheme',
 | 
			
		||||
 | 
			
		||||
@@ -50,6 +54,37 @@ const commands: Command[] = [
 | 
			
		||||
    callback: resetEditorScale,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.getIndent',
 | 
			
		||||
    callback: getIndent,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.runLineContext',
 | 
			
		||||
    callback: runLineContext,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.runCursorContext',
 | 
			
		||||
    callback: runCursorContext
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const navCommands: CommandEntry[] = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.openWebview',
 | 
			
		||||
    callback: openWebview,
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.openKeyBindWin',
 | 
			
		||||
    callback: () => openKeyBindWin('Windows')
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.openKeyBindMac',
 | 
			
		||||
    callback: () => openKeyBindWin('Mac'),
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  //Navigation Keys......
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.showAllSymbols',
 | 
			
		||||
@@ -102,7 +137,7 @@ const commands: Command[] = [
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.getuickInputBack',
 | 
			
		||||
    name: 'mind-reader.getQuickInputBack',
 | 
			
		||||
    callback: () => vscode.commands.executeCommand('workbench.action.quickInputBack'),
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@@ -110,18 +145,38 @@ const commands: Command[] = [
 | 
			
		||||
    name: 'mind-reader.navigateForward',
 | 
			
		||||
    callback: () => vscode.commands.executeCommand('workbench.action.navigateForward'),
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const hubCommands: CommandEntry[] = [
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.runLineContext',
 | 
			
		||||
    callback: runLineContext,
 | 
			
		||||
    name: 'mind-reader.connectHub',
 | 
			
		||||
    callback: connectHub
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.runCursorContext',
 | 
			
		||||
    callback: runCursorContext
 | 
			
		||||
    name: 'mind-reader.disconnectHub',
 | 
			
		||||
    callback: disconnectHub
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.getIndent',
 | 
			
		||||
    callback: getIndent
 | 
			
		||||
  }
 | 
			
		||||
    name: 'mind-reader.uploadCurrentFile',
 | 
			
		||||
    callback: uploadCurrentFile
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.runProgram',
 | 
			
		||||
    callback: runProgram
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.stopExecution',
 | 
			
		||||
    callback: stopExecution
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    name: 'mind-reader.deleteProgram',
 | 
			
		||||
    callback: deleteProgram
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// COMMAND CALLBACK IMPLEMENTATIONS
 | 
			
		||||
@@ -150,6 +205,38 @@ function resetEditorScale(): void {
 | 
			
		||||
  vscode.commands.executeCommand('workbench.action.zoomReset');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    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');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getWebviewContent(filepath: string) {
 | 
			
		||||
  return fs.readFileSync(filepath, {encoding: 'utf-8'});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getIndent(): void {
 | 
			
		||||
  let editor = vscode.window.activeTextEditor;
 | 
			
		||||
  if(editor)
 | 
			
		||||
@@ -231,17 +318,17 @@ function createContextString(context: pl.LexNode[], line: number): string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// find up to `n` words around the cursor, where `n` is
 | 
			
		||||
// the value of `#mindreader.reader.contextWindow`
 | 
			
		||||
// the value of `#mindReader.reader.contextWindow`
 | 
			
		||||
function runCursorContext(): void {
 | 
			
		||||
  let editor = vscode.window.activeTextEditor;
 | 
			
		||||
  if (!editor) {
 | 
			
		||||
    vscode.window.showErrorMessage("RunCursorContext: No Active Editor");
 | 
			
		||||
    vscode.window.showErrorMessage('RunCursorContext: No Active Editor');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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')!;
 | 
			
		||||
  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
 | 
			
		||||
@@ -296,4 +383,140 @@ function runCursorContext(): void {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default commands;
 | 
			
		||||
// Current connected hub
 | 
			
		||||
let hub: HubManager | null = null;
 | 
			
		||||
 | 
			
		||||
// TODO: port option
 | 
			
		||||
async function connectHub(): Promise<void> {
 | 
			
		||||
  if (hub) {
 | 
			
		||||
    vscode.window.showWarningMessage('LEGO Hub is already connected, reconnecting...');
 | 
			
		||||
    disconnectHub();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
      const ports = await HubManager.queryPorts();
 | 
			
		||||
 | 
			
		||||
      if (ports.length === 0) {
 | 
			
		||||
        vscode.window.showErrorMessage('No ports found. Is the LEGO Hub connected?');
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let portPath: string | undefined = vscode.workspace.getConfiguration('mindReader.connection').get('portPath');
 | 
			
		||||
 | 
			
		||||
      if (!portPath) {
 | 
			
		||||
        let slots: vscode.QuickPickItem[] = [];
 | 
			
		||||
        for (const port of ports) {
 | 
			
		||||
          slots.push({ label: port.path });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let picked = await vscode.window.showQuickPick(slots);
 | 
			
		||||
 | 
			
		||||
        if (!picked) {
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        portPath = picked.label;
 | 
			
		||||
      }
 | 
			
		||||
      hub = await HubManager.create(portPath);
 | 
			
		||||
      vscode.window.showInformationMessage('LEGO Hub connected');
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    vscode.window.showErrorMessage('Could not connect to LEGO Hub');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function disconnectHub(): Promise<void> {
 | 
			
		||||
  if (!hub || !hub.isOpen()) {
 | 
			
		||||
    vscode.window.showErrorMessage('LEGO Hub is not connected');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await hub.close();
 | 
			
		||||
  hub = null;
 | 
			
		||||
  vscode.window.showInformationMessage('LEGO Hub disconnected');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function uploadCurrentFile(): Promise<void> {
 | 
			
		||||
  if (!hub || !hub.isOpen()) {
 | 
			
		||||
    vscode.window.showErrorMessage('LEGO Hub is not connected!');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!vscode.window.activeTextEditor) {
 | 
			
		||||
    vscode.window.showErrorMessage('No active text editor');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const currentFilePath = vscode.window.activeTextEditor.document.fileName;
 | 
			
		||||
 | 
			
		||||
  if (currentFilePath) {
 | 
			
		||||
    // construct quickpick
 | 
			
		||||
    const slots: vscode.QuickPickItem[] = [];
 | 
			
		||||
    for (let i = 0; i < 10; i++) {
 | 
			
		||||
      slots.push({ label: i.toString() });
 | 
			
		||||
    }
 | 
			
		||||
    const slotID = await vscode.window.showQuickPick(slots, { canPickMany: false });
 | 
			
		||||
 | 
			
		||||
    if (!slotID) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO: progress bar?
 | 
			
		||||
    vscode.window.showInformationMessage('Uploading current file');
 | 
			
		||||
    await hub.uploadFile(currentFilePath, parseInt(slotID.label), path.basename(currentFilePath));
 | 
			
		||||
    vscode.window.showInformationMessage(path.basename(currentFilePath) + ' uploaded to slot ' + slotID.label);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: find empty slots
 | 
			
		||||
async function runProgram(): Promise<void> {
 | 
			
		||||
  if (!hub || !hub.isOpen()) {
 | 
			
		||||
    vscode.window.showErrorMessage('LEGO Hub is not connected!');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const slots: vscode.QuickPickItem[] = [];
 | 
			
		||||
  // construct quickpick
 | 
			
		||||
  for (let i = 0; i < 10; i++) {
 | 
			
		||||
    slots.push({ label: i.toString() });
 | 
			
		||||
  }
 | 
			
		||||
  const slotID = await vscode.window.showQuickPick(slots, { canPickMany: false });
 | 
			
		||||
 | 
			
		||||
  if (!slotID) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  vscode.window.showInformationMessage('Running program ' + slotID.label);
 | 
			
		||||
  await hub.programExecute(parseInt(slotID.label));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function stopExecution(): Promise<void> {
 | 
			
		||||
  if (!hub || !hub.isOpen()) {
 | 
			
		||||
    vscode.window.showErrorMessage('LEGO Hub is not connected!');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await hub.programTerminate();
 | 
			
		||||
  vscode.window.showInformationMessage('Execution stopped');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: find slots from status
 | 
			
		||||
async function deleteProgram(): Promise<void> {
 | 
			
		||||
  if (!hub || !hub.isOpen()) {
 | 
			
		||||
    vscode.window.showErrorMessage('LEGO Hub is not connected!');
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const slots: vscode.QuickPickItem[] = [];
 | 
			
		||||
  // construct quickpick
 | 
			
		||||
  for (let i = 0; i < 10; i++) {
 | 
			
		||||
    slots.push({ label: i.toString() });
 | 
			
		||||
  }
 | 
			
		||||
  const slotID = await vscode.window.showQuickPick(slots, { canPickMany: false });
 | 
			
		||||
 | 
			
		||||
  if (!slotID) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await hub.deleteProgram(parseInt(slotID.label));
 | 
			
		||||
  vscode.window.showInformationMessage('Deleted program ' + slotID.label);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,27 @@
 | 
			
		||||
import * as vscode from 'vscode';
 | 
			
		||||
 | 
			
		||||
import * as pl from './pylex';
 | 
			
		||||
import commands from './commands';
 | 
			
		||||
 | 
			
		||||
import AccessNodeProvider from './accessNodeProvider'
 | 
			
		||||
import { accessCommands, hubCommands, navCommands } from './commands';
 | 
			
		||||
 | 
			
		||||
import CommandNodeProvider from './commandNodeProvider';
 | 
			
		||||
import Logger from './log';
 | 
			
		||||
 | 
			
		||||
// Output Logger
 | 
			
		||||
const product: string = vscode.workspace.getConfiguration('mindReader').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) {
 | 
			
		||||
  console.log('Congratulations, your extension "mind-reader" is now active!');
 | 
			
		||||
  vscode.window.showInformationMessage('Mind_Reader is loaded!');
 | 
			
		||||
 | 
			
		||||
  parser.parse('Beep Boop');
 | 
			
		||||
 | 
			
		||||
  let allCommands = accessCommands.concat(hubCommands).concat(navCommands);
 | 
			
		||||
 | 
			
		||||
  // Register Commands
 | 
			
		||||
  commands.forEach(command => {
 | 
			
		||||
  allCommands.forEach(command => {
 | 
			
		||||
    let disposable = vscode.commands.registerCommand(
 | 
			
		||||
      command.name,
 | 
			
		||||
      command.callback
 | 
			
		||||
@@ -22,9 +29,11 @@ export function activate(context: vscode.ExtensionContext) {
 | 
			
		||||
    context.subscriptions.push(disposable);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  let provider = new AccessNodeProvider();
 | 
			
		||||
  vscode.window.registerTreeDataProvider('accessActions', provider);
 | 
			
		||||
  let accessProvider = new CommandNodeProvider(accessCommands);
 | 
			
		||||
  vscode.window.registerTreeDataProvider('accessActions', accessProvider);
 | 
			
		||||
 | 
			
		||||
  let hubProvider = new CommandNodeProvider(hubCommands);
 | 
			
		||||
  vscode.window.registerTreeDataProvider('hubActions', hubProvider);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function deactivate() {}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										391
									
								
								src/hubManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								src/hubManager.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,391 @@
 | 
			
		||||
import * as SerialPort from 'serialport';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
 | 
			
		||||
import { logger } from './extension';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @type RPCRequest an RPC request message
 | 
			
		||||
 *
 | 
			
		||||
 * @prop {string} `'m'` method to invoke.
 | 
			
		||||
 * @prop {Object?} `'p'` optional parameters for method.
 | 
			
		||||
 * @prop {string|null?} `'i'` optional request ID.
 | 
			
		||||
 */
 | 
			
		||||
type RPCRequest = {
 | 
			
		||||
  'm': string;
 | 
			
		||||
  'p'?: Object;
 | 
			
		||||
  'i'?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @type RPCResponse an RPC response message
 | 
			
		||||
 *
 | 
			
		||||
 * @prop {any?} `'r'` RPC response body
 | 
			
		||||
 * @prop {Object?} `'e'` RPC error body
 | 
			
		||||
 * @prop {string|null} `'i'` required response ID.
 | 
			
		||||
 */
 | 
			
		||||
type RPCResponse = {
 | 
			
		||||
  'r'?: any;
 | 
			
		||||
  'e'?: Object;
 | 
			
		||||
  'i': string | null;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Manages sending and receiving of JSON Remote Procedure Call (JSON-RPC) protocol messages
 | 
			
		||||
 * to the Hub.
 | 
			
		||||
 */
 | 
			
		||||
export default class HubManager {
 | 
			
		||||
  private port: SerialPort;
 | 
			
		||||
  private receiveBuffer: string = ''; // buffer for in-flight messages
 | 
			
		||||
  private pendingRequests = new Map<string, [(result: any) => void, (error: string) => void]>(); // lists of requests that are still pending
 | 
			
		||||
 | 
			
		||||
  // ======================== INSTANCE METHODS ========================
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Private constructor, use static `create` + `init`
 | 
			
		||||
   */
 | 
			
		||||
  private constructor(public portPath: string) { }
 | 
			
		||||
 | 
			
		||||
  public isOpen(): boolean {
 | 
			
		||||
    return this.port.isOpen;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Handle a received data chunk from the serial port
 | 
			
		||||
   *
 | 
			
		||||
   * @param `data` Data received from serial port
 | 
			
		||||
   */
 | 
			
		||||
  private async receiveData(data: string) {
 | 
			
		||||
    // add data to buffer
 | 
			
		||||
    this.receiveBuffer += data;
 | 
			
		||||
 | 
			
		||||
    // get full lines in buffer
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    for (const msg of msgs) {
 | 
			
		||||
      // check if this msg is a response to a pending request
 | 
			
		||||
      try {
 | 
			
		||||
        let json: { [key: string]: any };
 | 
			
		||||
 | 
			
		||||
        json = JSON.parse(msg);
 | 
			
		||||
 | 
			
		||||
        let id = json['i'];
 | 
			
		||||
        if (id && this.pendingRequests.has(id)) {
 | 
			
		||||
          // a request is waiting on this response
 | 
			
		||||
          let [resolve, reject] = this.pendingRequests.get(id) ?? [];
 | 
			
		||||
 | 
			
		||||
          if (json['e'] && reject) {
 | 
			
		||||
            // error
 | 
			
		||||
            reject(Buffer.from(json['e'], 'base64').toString());
 | 
			
		||||
          } else if (resolve) {
 | 
			
		||||
            resolve(json['r']);
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          this.pendingRequests.delete(id);
 | 
			
		||||
        } else if (json['m']) {
 | 
			
		||||
          // Print errors
 | 
			
		||||
          const params = json['p'];
 | 
			
		||||
          switch (json['m']) {
 | 
			
		||||
            case 'user_program_error':
 | 
			
		||||
              logger.error(Buffer.from(params[3], 'base64').toString());
 | 
			
		||||
              logger.error(Buffer.from(params[4], 'base64').toString());
 | 
			
		||||
              break;
 | 
			
		||||
            case 'runtime_error':
 | 
			
		||||
              logger.error(Buffer.from(params[3], 'base64').toString());
 | 
			
		||||
              break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        console.log('Could not parse JSON:', msg);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initializes a created HubManager with the current option settings
 | 
			
		||||
   */
 | 
			
		||||
  public async init(): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      this.port = new SerialPort(
 | 
			
		||||
        this.portPath,
 | 
			
		||||
        {
 | 
			
		||||
          autoOpen: true,
 | 
			
		||||
          baudRate: 112500,
 | 
			
		||||
        }
 | 
			
		||||
      );
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      // error during port opening
 | 
			
		||||
      throw err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.port.setEncoding('utf-8');
 | 
			
		||||
 | 
			
		||||
    // push lines received to data queue
 | 
			
		||||
 | 
			
		||||
    let mgr = this;
 | 
			
		||||
    this.port.on('data', data => {
 | 
			
		||||
      mgr.receiveData(data);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async close(): Promise<void> {
 | 
			
		||||
    this.port.close(console.error);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Send an RPC message. The corresponding Promise resolve
 | 
			
		||||
   * is saved into the `pendingRequests` map. When an inbound
 | 
			
		||||
   * message is found that matches an ID in `pendingRequests`,
 | 
			
		||||
   * the corresponding resolve is called. So, even though
 | 
			
		||||
   * the `resolve` call does not appear explicitly here, it *does*
 | 
			
		||||
   * get resolved at the appropriate time.
 | 
			
		||||
   *
 | 
			
		||||
   * @param `proc` Procedure to execute
 | 
			
		||||
   * @param `params` Optional parameters for the procedure
 | 
			
		||||
   * @param `id` The ID to use for the RPC message. Use null to indicate no ID/notification message.
 | 
			
		||||
   *             If neither a string or `null` is passed, an ID is automatically generated.
 | 
			
		||||
   */
 | 
			
		||||
  public async send(request: RPCRequest): Promise<RPCResponse> {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      if (request['i'] === undefined) {
 | 
			
		||||
        // generate an ID
 | 
			
		||||
        request['i'] = 'mind-reader-' + HubManager.randomID();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // write JSON to port
 | 
			
		||||
 | 
			
		||||
      this.pendingRequests.set(request['i'], [resolve, reject]);
 | 
			
		||||
      this.port.write(JSON.stringify(request));
 | 
			
		||||
      this.port.write('\r');
 | 
			
		||||
      this.port.drain();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async uploadFile(file: string, slotid: number, name?: string, autoStart: boolean = false) {
 | 
			
		||||
    const fileStats = fs.statSync(file);
 | 
			
		||||
    name = name || file;
 | 
			
		||||
 | 
			
		||||
    const ack: {[key: string]: any} = await this.startWriteProgram(
 | 
			
		||||
      name,
 | 
			
		||||
      fileStats.size,
 | 
			
		||||
      slotid,
 | 
			
		||||
      fileStats.birthtime.getTime(),
 | 
			
		||||
      fileStats.mtime.getTime()
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const blockSize = ack.blocksize;
 | 
			
		||||
    const transferid = ack.transferid;
 | 
			
		||||
 | 
			
		||||
    let dataStream = fs.createReadStream(file, { highWaterMark: blockSize });
 | 
			
		||||
    for await (const data of dataStream) {
 | 
			
		||||
      await this.writePackage(data, transferid);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (autoStart) {
 | 
			
		||||
      return Promise.resolve(await this.programExecute(slotid));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ========================= Hub Methods =========================
 | 
			
		||||
  //
 | 
			
		||||
  // These methods each handle a single RPC method's communication.
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Execute a program that is saved on the hub
 | 
			
		||||
   *
 | 
			
		||||
   * @param `slotid` Slot ID of the program to run
 | 
			
		||||
   */
 | 
			
		||||
  public async programExecute(slotid: number): Promise<RPCResponse> {
 | 
			
		||||
    return new Promise(async (resolve) => {
 | 
			
		||||
        let response = await this.send({
 | 
			
		||||
          'm': 'program_execute',
 | 
			
		||||
          'p': {
 | 
			
		||||
            'slotid': slotid
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        resolve(response);
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Terminate a currently running program
 | 
			
		||||
   */
 | 
			
		||||
  public async programTerminate(): Promise<RPCResponse> {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({ 'm': 'program_terminate' })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get the storage status of the hub
 | 
			
		||||
   *
 | 
			
		||||
   * TODO: fill with actual example
 | 
			
		||||
   * slot
 | 
			
		||||
   * decoded name
 | 
			
		||||
   * size
 | 
			
		||||
   * last modified
 | 
			
		||||
   * project_id
 | 
			
		||||
   * type
 | 
			
		||||
   */
 | 
			
		||||
  public async getStorageStatus(): Promise<RPCResponse> {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({ 'm': 'get_storage_status' })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Notify the Hub that a program write is about to occur.
 | 
			
		||||
   *
 | 
			
		||||
   * @param `name` Name of the program.
 | 
			
		||||
   * @param `name` Size of the program TODO: in bytes?
 | 
			
		||||
   * @param `slotID` Slot ID to write the program to
 | 
			
		||||
   * @param `created` Creation timestamp
 | 
			
		||||
   * @param `modified` Modified timestamp
 | 
			
		||||
   */
 | 
			
		||||
  public async startWriteProgram(
 | 
			
		||||
    name: string,
 | 
			
		||||
    size: number,
 | 
			
		||||
    slotID: number,
 | 
			
		||||
    created: number,
 | 
			
		||||
    modified: number
 | 
			
		||||
  ): Promise<RPCResponse> {
 | 
			
		||||
    // file meta data
 | 
			
		||||
    let stat  = {
 | 
			
		||||
      'created': created,
 | 
			
		||||
      'modified': modified,
 | 
			
		||||
      'name': name,
 | 
			
		||||
      'type': 'python', // always python
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/naming-convention
 | 
			
		||||
      'project_id': HubManager.randomID(16),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({
 | 
			
		||||
        'm': 'start_write_program',
 | 
			
		||||
        'p': {
 | 
			
		||||
          'slotid': slotID,
 | 
			
		||||
          'size': size,
 | 
			
		||||
          'meta': stat
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Write a package chunk of a file
 | 
			
		||||
   *
 | 
			
		||||
   * @param `data` Chunk of data to write
 | 
			
		||||
   * @param `transferID` The transfer id for the current transfer
 | 
			
		||||
   */
 | 
			
		||||
  public async writePackage(data: string, transferID: string): Promise<RPCResponse> {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({
 | 
			
		||||
        'm': 'write_package',
 | 
			
		||||
        'p': {
 | 
			
		||||
          'data': Buffer.from(data, 'utf-8').toString('base64'),
 | 
			
		||||
          'transferid': transferID
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Move a program from slot `fromSlot` to slot `toSlot`.
 | 
			
		||||
   * TODO: verify
 | 
			
		||||
   * If the destination already has a program stored in it, the two programs
 | 
			
		||||
   * are swapped
 | 
			
		||||
   *
 | 
			
		||||
   * @param `fromSlot` slot to move from
 | 
			
		||||
   * @param `toSlot` slot to move to
 | 
			
		||||
   */
 | 
			
		||||
  public async moveProgram(oldSlotID: number, newSlotID: number) {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({
 | 
			
		||||
        'm': 'move_project',
 | 
			
		||||
        'p': {
 | 
			
		||||
          // eslint-disable-next-line @typescript-eslint/naming-convention
 | 
			
		||||
          'old_slotid': oldSlotID,
 | 
			
		||||
          // eslint-disable-next-line @typescript-eslint/naming-convention
 | 
			
		||||
          'new_slotid': newSlotID
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Delete a program from the slot on the Hub
 | 
			
		||||
   *
 | 
			
		||||
   * @param `slotID` the slot to delete
 | 
			
		||||
   */
 | 
			
		||||
  public async deleteProgram(slotID: number) {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({
 | 
			
		||||
        'm': 'remove_project',
 | 
			
		||||
        'p': {
 | 
			
		||||
          'slotid': slotID
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get firmware info for the connected device
 | 
			
		||||
   */
 | 
			
		||||
  public async getHubInfo() {
 | 
			
		||||
    return Promise.resolve(
 | 
			
		||||
      await this.send({
 | 
			
		||||
        'm': 'get_hub_info'
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // ======================== INSTANCE METHODS ========================
 | 
			
		||||
 | 
			
		||||
  public static async create(portPath: string): Promise<HubManager> {
 | 
			
		||||
    return new Promise(async (resolve) => {
 | 
			
		||||
      try {
 | 
			
		||||
        let mgr = new HubManager(portPath);
 | 
			
		||||
        await mgr.init();
 | 
			
		||||
 | 
			
		||||
        return resolve(mgr);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
 | 
			
		||||
        // could not connect to port
 | 
			
		||||
        throw err;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Returns a list of serial devices
 | 
			
		||||
   */
 | 
			
		||||
  public static async queryPorts() {
 | 
			
		||||
    // get all ports
 | 
			
		||||
    let ports = await SerialPort.list();
 | 
			
		||||
 | 
			
		||||
    // filter by manufacturer
 | 
			
		||||
    return Promise.resolve(ports);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Generate a random identifier consisting of lowercase
 | 
			
		||||
   * letters, numbers, and underscores
 | 
			
		||||
   *
 | 
			
		||||
   * @param `len` The length of the string to generate
 | 
			
		||||
   */
 | 
			
		||||
  static randomID(len: number = 4): string {
 | 
			
		||||
    let result = '';
 | 
			
		||||
    let characters = 'abcdefghijklmnopqrstuvwxyz0123456789_';
 | 
			
		||||
    for (let i = 0; i < len; i++) {
 | 
			
		||||
      result += characters[Math.floor(Math.random() * characters.length)];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								src/log.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/log.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import * as vscode from 'vscode';
 | 
			
		||||
 | 
			
		||||
export default class Logger {
 | 
			
		||||
  constructor(
 | 
			
		||||
    public readonly outputChannel: vscode.OutputChannel
 | 
			
		||||
  ) { }
 | 
			
		||||
 | 
			
		||||
  public log(text: string): void {
 | 
			
		||||
    this.outputChannel.appendLine(text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public info(text: string): void {
 | 
			
		||||
    this.outputChannel.appendLine('[INFO]\r' + text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public warn(text: string): void {
 | 
			
		||||
    this.outputChannel.appendLine('[WARNING]\r' + text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public error(text: string): void {
 | 
			
		||||
    this.outputChannel.appendLine('[ERROR]\r' + text);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user