├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── keymaps └── platformio-ide-terminal.cson ├── lib ├── dialog.coffee ├── input-dialog.coffee ├── platformio-ide-terminal.coffee ├── process.coffee ├── rename-dialog.coffee ├── status-bar.coffee ├── status-icon.coffee └── view.coffee ├── menus └── platformio-ide-terminal.cson ├── package.json ├── resources ├── demo copy.gif ├── demo.gif ├── full_screen_demo.png ├── insert_selected_text.gif ├── insert_text.png ├── map_terminals_to_auto_open.gif ├── map_terminals_to_file.gif ├── map_terminals_to_folder.gif ├── plus-icon.png ├── red-x.png ├── sorting.gif ├── special_keys.gif ├── status-bar.png ├── status-icon.png ├── status-icon_color_coding.png ├── status-icon_rename-dialog.png ├── status-icon_rename.png ├── terminal_title.png └── terminal_with_status-bar.png ├── spec ├── platformio-ide-terminal-spec.coffee └── platformio-ide-terminal-view-spec.coffee └── styles ├── platformio-ide-terminal.less ├── themes.less └── themes ├── christmas.less ├── city-lights.less ├── dracula.less ├── grass.less ├── homebrew.less ├── inverse.less ├── linux.less ├── man-page.less ├── novel.less ├── ocean.less ├── one-dark.less ├── predawn.less ├── pro.less ├── red-sands.less ├── red.less ├── silver-aerogel.less ├── solarized-dark.less ├── solarized-light.less ├── solid-colors.less └── standard.less /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | - [ ] I have reset Atom to defaults prior to submitting report. 5 | - [ ] I have not reset Atom to defaults prior to submitting report. 6 | ### Description 7 | 8 | 9 | 10 | ### Steps to reproduce 11 | 13 | 1. 14 | 2. 15 | 3. 16 | 17 | 18 | **Expected behavior:** 19 | 20 | 21 | **Actual behavior:** 22 | 23 | 24 | **Reproduces how often:** 25 | 26 | 27 | ### Versions 28 | 32 | 33 | ``` 34 | > atom --version 35 | 36 | ``` 37 | ``` 38 | > apm --version 39 | 40 | ``` 41 | OS name and version: 42 | Platformio-ide-terminal version: 43 | 44 | 45 | ### Additional Information 46 | 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Pull request details 2 | 3 | - [ ] This PR is a bug fix 4 | - [ ] This PR implements a new feature or introduces new behavior. 5 | 6 | ### Description of the change 7 | 9 | 10 | ### Notes 11 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.10.1 - PlatformIO 2 | 3 | * Fix an issue with missed pre-built node-pty modules 4 | 5 | ## 2.10.0 - PlatformIO 6 | 7 | * Add setting to disable toolbar buttons (issue #794) 8 | * Update themes on config changes (issue #793) 9 | 10 | ## 2.9.4 - PlatformIO 11 | 12 | * Add compatibility for Atom Electron 3.x builds 13 | 14 | ## 2.9.3 - PlatformIO 15 | 16 | * Fix broken terminal 17 | 18 | ## 2.9.2 - PlatformIO 19 | 20 | * Add compatibility for Atom 1.39 21 | 22 | ## 2.9.1 - PlatformIO 23 | 24 | * Add "solarized-light" theme 25 | * Fix an issue with a shortcut when reopening last item and new terminal for macOS 26 | 27 | ## 2.9.0 - PlatformIO 28 | 29 | * Use current Atom styles for standard theme (issue #607) 30 | * Switch to "node-pty-prebuilt" (issues #551, #585, #632) 31 | 32 | ## 2.8.4 - PlatformIO 33 | 34 | * Add city-lights theme 35 | * atom-space-pen-views: bump to v2.2.0 36 | 37 | ## 2.8.3 - PlatformIO 38 | 39 | * Add compatibility for Atom 1.28 40 | 41 | ## 2.8.2 - PlatformIO 42 | 43 | * Remove the 3D shadow (#535) 44 | * Fix "Copy and Paste" (#523) 45 | * Fix to enable insert-selected-text on Mac (#534) 46 | 47 | ## 2.8.1 - PlatformIO 48 | 49 | * Add compatibility with Atom 1.25 50 | 51 | ## 2.8.0 - PlatformIO 52 | 53 | * Add "clear" item to context menu (re-start terminal) 54 | * Check if undefined pathname before using for `path.basename` (#434) 55 | * Fix theme colors for one-dark and solarized-dark (#424) 56 | 57 | ## 2.7.0 - PlatformIO 58 | 59 | * Add extra environment variable to shell execution with `Shell Environment Variables` setting 60 | * Fix selectToCopy turns space into non-breaking-space (#211) 61 | * Fix fullscreen toggle (#227) 62 | 63 | ## 2.6.0 - PlatformIO 64 | 65 | * Add predawn theme (#296) 66 | * Optional --login shell (#294) 67 | * Toolbar button with atom styleguide (#345) 68 | * Fix labels rendering outside of Atom's 'editor mini' panel (#339) 69 | * Fix "Some installed packages could not be loaded..." with a new Atom 1.19 70 | 71 | ## 2.5.5 - PlatformIO 72 | 73 | * Add compatibility for Atom 1.19 74 | 75 | ## 2.5.4 - PlatformIO 76 | 77 | * Updated One Dark theme foreground color to match Atom 78 | * Fixed deprecation warning thrown in Atom Beta >=1.13 79 | 80 | ## 2.5.3 - PlatformIO 81 | 82 | * Fix broken "Terminal: Focus" command 83 | 84 | ## 2.5.2 - PlatformIO 85 | 86 | * Fix issue when opened terminal steals focus after focusing Atom 87 | 88 | ## 2.5.1 - PlatformIO 89 | 90 | * Fix for delta calculation in resizePanel 91 | * Fix focus (ctrl+alt+F) shortcut behavior 92 | 93 | ## 2.5.0 - PlatformIO 94 | 95 | * New PlatformIO IDE Terminal API Provider 96 | * Load terminal after Atom shell-environment is loaded 97 | * Fix status bar alignment problem on native-ui theme 98 | 99 | ## 2.4.0 - PlatformIO 100 | 101 | * Add terminal focus command and keybinding 102 | * Fix issue when running `electron` command 103 | 104 | ## 2.3.1 - PlatformIO 105 | 106 | * Use /bin/bash shell by default if process.env.SHELL is not defined 107 | 108 | ## 2.3.0 - PlatformIO 109 | 110 | * New option: Copy text to clipboard on selection (enabled by default) 111 | 112 | ## 2.2.3 - PlatformIO 113 | 114 | * Add Christmas and One-Dark themes 115 | * Add compatibility for Atom 1.14 and Windows x64 116 | 117 | ## 2.2.2 - PlatformIO 118 | 119 | * Fix "Uncaught TypeError: Cannot read property 'focus' of null" ([issue #29](https://github.com/platformio/platformio-atom-ide-terminal/issues/29)) 120 | 121 | ## 2.2.1 - PlatformIO 122 | 123 | * Add compatibility for Atom 1.12, Windows and macOS 124 | 125 | ## 2.2.0 - PlatformIO 126 | 127 | * Add Solarized-Dark theme 128 | * Make `insert-custom-text` context aware 129 | 130 | ## 2.1.1 - PlatformIO 131 | 132 | * Change "red-x" to default Atom theme 133 | * Move the status bar icons to the left of the file path 134 | 135 | ## 2.1.0 - PlatformIO 136 | 137 | * Add Linux theme 138 | * Presets with the custom commands/texts (`Settings > Custom Texts`) 139 | 140 | ## 2.0.10 - PlatformIO 141 | 142 | * Add fullscreen command 143 | 144 | ## 2.0.9 - PlatformIO 145 | 146 | * Fix PTY.js binary finder for Windows 147 | 148 | ## 2.0.8 - PlatformIO 149 | 150 | * Rewrite PTY.js installer for NodeJS 0.10.x 151 | 152 | ## 2.0.7 - PlatformIO 153 | 154 | * Update PTY.js dependency 155 | 156 | ## 2.0.6 - PlatformIO 157 | * Improve foreground installation of PTY.js 158 | 159 | ## 2.0.5 - PlatformIO 160 | * Add hook for node-gyp with pre-built binaries 161 | 162 | ## 2.0.4 - PlatformIO 163 | * Switch to PlatformIO PTY.js fork 164 | 165 | ## 2.0.3 - PlatformIO 166 | * Extend API with getTerminalViews 167 | 168 | ## 2.0.2 - PlatformIO 169 | * Add API that allows any package to make a terminal, pass it a command and see the output 170 | * Use Atom's native status bar instead own (save space) 171 | * Use direct HTTP links to dependent packages instead `git+https` 172 | 173 | ## v0.14.5 - Patch 174 | * Fix key-presses deselecting lines from the terminal 175 | * Add alt-(arrow) and alt-(click) for moving cursor in terminal 176 | * Improve `clear` command handling 177 | * Make sure initial height is to the nearest row 178 | 179 | ## v0.14.4 - Patch 180 | * Fix `reset` and `clear` command issues 181 | * Improve `cmd-k` clear command 182 | * Insert text as a command by default 183 | * Match text editor font family for consistency 184 | * Add font size setting back 185 | * Destroy panel on terminal destroy 186 | * Allow any css value for default panel height 187 | 188 | ## v0.14.3 - Patch 189 | * Remove extra logs 190 | * Bug fixes 191 | * Match Atom's font size 192 | 193 | ## v0.14.2 - Patch 194 | * Bring back default selection process 195 | * Improvements to terminal (jeremyramin/term.js) 196 | * Fix several bugs 197 | * Clean up menus 198 | 199 | ## v0.14.1 - Patch 200 | * Recompile binaries for Atom 1.2.0 201 | * Fix focus error 202 | * Return focus to terminal on window focus 203 | * Minor fixes to text area and terminal resizing 204 | 205 | ## v0.14.0 - Beta Release 206 | * Added support for non-English characters 207 | * Thanks to @yoshiokatsuneo for [his work](https://github.com/jeremyramin/term.js/commit/e851ea232a114902ea6a8e5cc8f7d34d07969c42). 208 | * Dead keys now work in the terminal 209 | * CJK (IME) should now work in the terminal 210 | * Keep terminals maximized on new terminal view 211 | * Improve terminal title handling 212 | * Fix fullscreen not focusing 213 | * Refactor dialog classes 214 | * Update Windows binaries 215 | 216 | ## v0.13.1 - Patch 217 | * Only run inserted text if `Run Inserted Text` is enabled 218 | * Improve terminal title handling 219 | * Make sure the terminal title does not stay behind on new title 220 | * Open the correct project folder for the active file 221 | * Post-install clean-up script for pty.js (see issue #71) 222 | 223 | ## v0.13.0 - Beta Release 224 | * Add support for alt+key combinations 225 | * Right alt for escape sequences 226 | * Fix terminal mapping toggle error when `Auto Open a New Terminal` is false 227 | * Improve cursor: 228 | * Preserve the color of the character the cursor is over 229 | * Fix terminal cursor background-color being overwritten 230 | * Only intercept ctrl+key combinations if ctrl is the only key being pressed 231 | 232 | ## v0.12.5 - Patch 233 | * Fix `ctrl` key intercepting to ignore `ctrl-shift` combinations 234 | * Fix `ctrl-s` pausing terminal on Linux systems 235 | 236 | ## v0.12.4 - Patch 237 | * Map terminals while the active terminal is hidden 238 | * Fix prev/next terminal switch 239 | * Improve cursor blink animation 240 | * New commands: 241 | * terminal-plus:close-all for closing all terminals 242 | * terminal-plus:rename for renaming the active terminal 243 | 244 | ## v0.12.3 - Patch 245 | * Blur terminal window on window blur 246 | * Adjust default ANSI colors for terminal 247 | * Confirm that the active terminal is not null 248 | 249 | ## v0.12.2 - Patch 250 | * Prevent terminal from intercepting alt+(key) events 251 | * Fixes broken copy and paste on Linux and Windows 252 | 253 | ## v0.12.1 - Patch 254 | * Make sure status icon tooltip dismisses when the status icon is detached 255 | * Fix copy and pasting bug with tabs 256 | * Improve active terminal system 257 | * Fix terminal resizing removing lines 258 | 259 | ## v0.12.0 - Beta Release 260 | * Clean up tooltip 261 | * Prevent file path insertion for empty file paths (Atom tabs) 262 | * Add experimental support for tab view 263 | 264 | ## v0.11.2 - Patch 265 | * Fix tooltips staying after the terminal has been closed 266 | 267 | ## v0.11.1 - Patch 268 | * Fix broken links in README 269 | 270 | ## v0.11.0 - Beta Release 271 | * Add insert text dialog for inserting special characters and running commands 272 | * Users can enable `Run Inserted Text` in the settings to have Terminal-Plus run inserted text as a command 273 | * Users can use the insert text dialog to type special characters 274 | * Center terminal lines in the terminal-view 275 | * Improved terminal mapping 276 | * Improve terminal view focusing 277 | * Do not steal focus for the cursor blink 278 | * Do not steal focus for text input 279 | 280 | ## v0.10.1 - Patch 281 | * Fix resizing bug 282 | * Fix language overwrite bug 283 | 284 | ## v0.10.0 - Beta Release 285 | * Added automatic terminal switching 286 | * Add CMD+K to clear terminal [Term.js fork] 287 | * Fix terminal errors relating to Atom setting project path to `atom://config` 288 | 289 | ## v0.9.1 - Patch 290 | * Fix bug where Atom rebuilds Terminal-Plus for every update 291 | * Fix status icon colors keypath 292 | 293 | ## v0.9.0 - Beta Release 294 | * Add support for custom ANSI color set 295 | * Fix `ctrl+c` (SIGINT) not working in bash 296 | * Update winpty module (for Windows) in pty.js 297 | * Fix issues with maintaining focus on the terminal 298 | 299 | ## v0.8.2 - Patch 300 | * Detect system language on OS X 301 | * Even finer scrolling algorithm implemented 302 | 303 | ## v0.8.1 - Patch 304 | * Disable double click on status icons 305 | 306 | ## v0.8.0 - Beta Release 307 | * Implement finer scrolling in dependencies 308 | 309 | ## v0.7.1 - Patch 310 | * Block resize and input when there is no pty process to message 311 | 312 | ## v0.7.0 - Beta Release 313 | * Add support for international characters 314 | * Make sure to declare the terminal as xterm-256color 315 | * Improve colors in xterm-256color 316 | * Set TERM_PROGRAM to Terminal-Plus 317 | 318 | ## v0.6.5 - Patch 319 | * Focus bug fix 320 | 321 | ## v0.6.4 - Patch 322 | * Fix terminal not scrolling for zsh shell with plugins 323 | 324 | ## v0.6.3 - Patch 325 | * Call super after overriding focus 326 | * Update the author's note with Windows 10 fix 327 | 328 | ## v0.6.2 - Patch 329 | * Fix path variable overwrite bug 330 | 331 | ## v0.6.1 - Patch 332 | * Fix text-wrap overflow hiding prompt 333 | 334 | ## v0.6.0 - Beta Release 335 | * Dynamic terminal view resizing 336 | 337 | ## v0.5.1 - Patch 338 | * Remove trailing whitespace from terminal rename 339 | 340 | ## v0.5.0 - Beta Release 341 | * Add terminal naming via the status icon 342 | 343 | ## v0.4.3 - Patch 344 | * Rebuild pty.js binaries for electron release 0.30.6 345 | * Requires Atom >= 1.0.12 346 | 347 | ## v0.4.2 - Patch 348 | * Specify commit for pty.js prebuilt 349 | 350 | ## v0.4.1 - Patch 351 | * Make button toolbar smaller by keeping buttons minimal 352 | * No more names next to button 353 | * Make button fit to icon 354 | * Use --login shell argument by default for bash and zsh 355 | 356 | ## v0.4.0 - Beta Release 357 | * Add prebuilt binaries for pty.js 358 | * Better support for systems without the tools needed to compile (Windows) 359 | 360 | ## v0.3.1 - Patch 361 | * Add warning for custom font family (must use monospaced font) 362 | 363 | ## v0.3.0 - Beta Release 364 | * Refactor resizing to snap to row 365 | * Fix cursor line being removed if blank 366 | * Possible fix for refresh error 367 | * Fix for improper resizing when displaying the terminal for the first time 368 | 369 | ## v0.2.0 - Beta Release 370 | * Bump up to minor version 2 371 | * New settings and features added 372 | * Bug fixes listed below in v0.1.x patches 373 | 374 | ## v0.1.10 - Patch 375 | * Add option to auto close terminal on shell process exit 376 | 377 | ## v0.1.9 - Patch 378 | * Add insert selected text (see commit) 379 | * Remove login command 380 | 381 | ## v0.1.8 - Patch 382 | * Remove quiet option from login 383 | * Disable resize and input on terminal exit 384 | 385 | ## v0.1.7 - Patch 386 | * Resize terminal on maximize and minimize 387 | * Fix powershell.exe resolve 388 | * Fix shell launch bugs 389 | 390 | ## v0.1.6 - Patch 391 | * Make sure to properly resize terminal on open 392 | 393 | ## v0.1.5 - Patch 394 | * On shell process exit, disable input to prevent error 395 | 396 | ## v0.1.4 - Patch 397 | * Make terminal scroll to bottom on input 398 | * Don't close the terminal view on process exit 399 | 400 | ## v0.1.3 - Patch 401 | * Add more features to README.md 402 | * Fix issue #1 403 | 404 | ## v0.1.2 - Patch 405 | * Absolute image source paths in README.md 406 | * Update image in color coding section 407 | 408 | ## v0.1.1 - Patch 409 | * Update the README.md and CHANGELOG.md 410 | 411 | ## v0.1.0 - Beta Release 412 | * Initial release 413 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Jeremy Ebneyamin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PlatformIO IDE Terminal 2 | A terminal package for Atom, complete with themes, API and more for [PlatformIO IDE](http://platformio.org/platformio-ide). 3 | 4 | ![demo](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/demo.gif) 5 | 6 | *[Nucleus Dark UI](https://atom.io/themes/nucleus-dark-ui) with [Atom Material Syntax](https://atom.io/themes/atom-material-syntax) and our Homebrew theme.* 7 | 8 | ## Install 9 | Ready to install? 10 | 11 | You can install via apm: `apm install platformio-ide-terminal` 12 | 13 | Or navigate to the install tab in Atom’s settings view, and search for `platformio-ide-terminal`. 14 | 15 | ## Caveats 16 | flatpak 17 | 18 | Some Atom installations via flatpak may have incompatibilites, including an inability to find host system binaries from select directories, such as /usr/*. If you encounter issues using platformio-ide-terminal with flatpak, such as missing binaries, that are normally accessible from the path you have set in your shell of choice, or if you are not able to change directory into some directories, you may be affected. You can confirm this by looking for the nfsnobody permissions from the root of your disk. You can workaround this by installing another version of atom that is not inside a sandbox container. See [here for full details.](https://github.com/platformio/platformio-atom-ide-terminal/issues/664) 19 | 20 | ## Usage 21 | 22 | `platformio-ide-terminal` stays in the bottom of your editor while you work. 23 | 24 | Click on a status icon to toggle that terminal (or ``ctrl-` ``). Right click the status icon for a list of available commands. From the right-click menu you can color code the status icon as well as hide or close the terminal instance. 25 | 26 | ### Terminal 27 | You can open the last active terminal with the `platformio-ide-terminal:toggle` command (Default:`` ctrl-` ``). If no terminal instances are available, then a new one will be created. The same toggle command is used to hide the currently active terminal. 28 | 29 | From there you can begin typing into the terminal. By default the terminal will change directory into the project folder if possible. The default working directory can be changed in the settings to the home directory or to the active file directory. 30 | 31 | [See available commands below](#commands). 32 | 33 | ## Features 34 | 35 | ### Full Terminal 36 | Every terminal is loaded with your system’s default initialization files. This ensures that you have access to the same commands and aliases as you would in your standard terminal. 37 | 38 | ### Themes 39 | The terminal is preloaded with several themes that you can choose from. Not satisfied? 40 | Use the following template in your stylesheet: 41 | ```css 42 | .platformio-ide-terminal .xterm { 43 | background-color: ; 44 | color: ; 45 | 46 | ::selection { 47 | background-color: ; 48 | } 49 | 50 | .terminal-cursor { 51 | background-color: ; 52 | } 53 | } 54 | ``` 55 | 56 | ### Process Titles 57 | By hovering over the terminal status icon, you can see which command process is currently running in the terminal. 58 | 59 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/terminal_title.png) 60 | 61 | ### Terminal Naming 62 | Need a faster way to figure out which terminal is which? Name your status icons! 63 | 64 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/status-icon_rename.png) 65 | 66 | Available via the status icon context menu. 67 | 68 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/status-icon_rename-dialog.png) 69 | 70 | ### Color Coding 71 | Color code your status icons! 72 | 73 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/status-icon_color_coding.png) 74 | 75 | The colors are customizable in the settings, however the color names remain the same in the context menu. 76 | 77 | ### Sorting 78 | Organize your open terminal instances by dragging and dropping them. 79 | 80 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/sorting.gif) 81 | 82 | ### Resizable 83 | You can resize the view vertically, or just maximize it with the maximize button. 84 | 85 | ### Working Directory 86 | You can set the default working directory for new terminals. By default this will be the project folder. 87 | 88 | ### File Dropping 89 | Dropping a file on the terminal will insert the file path into the input. This works with external files, tabs from the Atom tab-view, and entries from the Atom tree-view. 90 | 91 | ### Insert Selected Text 92 | Insert and run selected text from your text editor by running the `platformio-ide-terminal:insert-selected-text` command (`ctrl-enter`). 93 | 94 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/insert_selected_text.gif) 95 | 96 | If you have text selected, it will insert your selected text into the active terminal and run it. 97 | If you don't have text selected it, will run the text on the line where your cursor is then proceed to the next line. 98 | 99 | ### Quick Command Insert 100 | Quickly insert a command to your active terminal by executing the `platformio-ide-terminal:insert-text` command. 101 | 102 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/insert_text.png) 103 | 104 | A dialog will pop up asking for the input to insert. If you have the `Run Inserted Text` option enabled in the settings (default is false), platformio-ide-terminal will automatically run the command for you. 105 | 106 | #### Support for Special Keys 107 | Support for IME, dead keys and other key combinations via the `Insert Text` dialog box. Just click the keyboard button in the top left of the terminal or set up a keymap to the `platformio-ide-terminal:insert-text` command. 108 | 109 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/special_keys.gif) 110 | 111 | Note: Make sure you have the `Run Inserted Command` toggle off otherwise it will run your inserted text. 112 | 113 | ### Map Terminal To 114 | Map your terminals to each file or folder you are working on for automatic terminal switching. 115 | 116 | #### File 117 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/map_terminals_to_file.gif) 118 | 119 | #### Folder 120 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/map_terminals_to_folder.gif) 121 | 122 | Toggling the `Auto Open a New Terminal (For Terminal Mapping)` option will have the mapping create a new terminal automatically for files and folders that don't have a terminal. The toggle is located right under the `Map Terminals To` option. 123 | 124 | ![](https://github.com/platformio/platformio-atom-ide-terminal/raw/master/resources/map_terminals_to_auto_open.gif) 125 | 126 | ## Commands 127 | | Command | Action | Default Keybind | 128 | |---------|--------|:-----------------:| 129 | | platformio-ide-terminal:new | Create a new terminal instance. | `ctrl-shift-t`
or
`cmd-shift-t` | 130 | | platformio-ide-terminal:toggle | Toggle the last active terminal instance.
**Note:** This will create a new terminal if it needs to. | `` ctrl-` ``
(Control + Backtick) | 131 | | platformio-ide-terminal:prev | Switch to the terminal left of the last active terminal. | `ctrl-shift-j`
or
`cmd-shift-j` | 132 | | platformio-ide-terminal:next | Switch to the terminal right of the last active terminal. | `ctrl-shift-k`
or
`cmd-shift-k` | 133 | | platformio-ide-terminal:insert-selected-text | Run the selected text as a command in the active terminal. | `ctrl-enter` | 134 | | platformio-ide-terminal:insert-text | Bring up an input box for using IME and special keys. | –––––––––––– | 135 | | platformio-ide-terminal:fullscreen | Toggle fullscreen for active terminal. | –––––––––––– | 136 | | platformio-ide-terminal:close | Close the active terminal. | `ctrl-shift-x`
or
`cmd-shift-x` | 137 | | platformio-ide-terminal:close-all | Close all terminals. | –––––––––––– | 138 | | platformio-ide-terminal:rename | Rename the active terminal. | –––––––––––– | 139 | 140 | --- 141 | A fork of [jeremyramin/terminal-plus](https://github.com/jeremyramin/terminal-plus). 142 | -------------------------------------------------------------------------------- /keymaps/platformio-ide-terminal.cson: -------------------------------------------------------------------------------- 1 | '.platform-darwin atom-workspace': 2 | 'cmd-alt-t': 'platformio-ide-terminal:new' 3 | 'cmd-shift-j': 'platformio-ide-terminal:prev' 4 | 'cmd-shift-k': 'platformio-ide-terminal:next' 5 | 'cmd-shift-x': 'platformio-ide-terminal:close' 6 | 'cmd-alt-f': 'platformio-ide-terminal:focus' 7 | 'ctrl-`': 'platformio-ide-terminal:toggle' 8 | 'ctrl-alt-enter': 'platformio-ide-terminal:insert-selected-text' 9 | 10 | '.platform-linux atom-workspace, .platform-win32 atom-workspace': 11 | 'alt-shift-t': 'platformio-ide-terminal:new' 12 | 'alt-shift-j': 'platformio-ide-terminal:prev' 13 | 'alt-shift-k': 'platformio-ide-terminal:next' 14 | 'alt-shift-x': 'platformio-ide-terminal:close' 15 | 'ctrl-enter': 'platformio-ide-terminal:insert-selected-text' 16 | 'ctrl-`': 'platformio-ide-terminal:toggle' 17 | 'ctrl-alt-f': 'platformio-ide-terminal:focus' 18 | 19 | '.platform-darwin .platformio-ide-terminal .terminal': 20 | 'cmd-c': 'platformio-ide-terminal:copy' 21 | 'cmd-v': 'platformio-ide-terminal:paste' 22 | 23 | '.platform-linux .platformio-ide-terminal .terminal, .platform-win32 .platformio-ide-terminal .terminal': 24 | 'alt-v': 'platformio-ide-terminal:paste' 25 | 'alt-c': 'platformio-ide-terminal:copy' 26 | -------------------------------------------------------------------------------- /lib/dialog.coffee: -------------------------------------------------------------------------------- 1 | {TextEditorView, View} = require 'atom-space-pen-views' 2 | 3 | module.exports = 4 | class Dialog extends View 5 | @content: ({prompt} = {}) -> 6 | @div class: 'platformio-ide-terminal-dialog', => 7 | @label prompt, class: 'icon', outlet: 'promptText' 8 | @subview 'miniEditor', new TextEditorView(mini: true) 9 | @label 'Escape (Esc) to exit', style: 'width: 50%;' 10 | @label 'Enter (\u21B5) to confirm', style: 'width: 50%; text-align: right;' 11 | 12 | initialize: ({iconClass, placeholderText, stayOpen} = {}) -> 13 | @promptText.addClass(iconClass) if iconClass 14 | atom.commands.add @element, 15 | 'core:confirm': => @onConfirm(@miniEditor.getText()) 16 | 'core:cancel': => @cancel() 17 | 18 | unless stayOpen 19 | @miniEditor.on 'blur', => @close() 20 | 21 | if placeholderText 22 | @miniEditor.getModel().setText placeholderText 23 | @miniEditor.getModel().selectAll() 24 | 25 | attach: -> 26 | @panel = atom.workspace.addModalPanel(item: this.element) 27 | @miniEditor.focus() 28 | @miniEditor.getModel().scrollToCursorPosition() 29 | 30 | close: -> 31 | panelToDestroy = @panel 32 | @panel = null 33 | panelToDestroy?.destroy() 34 | atom.workspace.getActivePane().activate() 35 | 36 | cancel: -> 37 | @close() 38 | -------------------------------------------------------------------------------- /lib/input-dialog.coffee: -------------------------------------------------------------------------------- 1 | Dialog = require "./dialog" 2 | os = require "os" 3 | 4 | module.exports = 5 | class InputDialog extends Dialog 6 | constructor: (@terminalView) -> 7 | super 8 | prompt: "Insert Text" 9 | iconClass: "icon-keyboard" 10 | stayOpen: true 11 | 12 | onConfirm: (input) -> 13 | if atom.config.get('platformio-ide-terminal.toggles.runInsertedText') 14 | eol = os.EOL 15 | else 16 | eol = '' 17 | 18 | data = "#{input}#{eol}" 19 | @terminalView.input data 20 | @cancel() 21 | -------------------------------------------------------------------------------- /lib/platformio-ide-terminal.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | statusBar: null 3 | 4 | activate: -> 5 | 6 | deactivate: -> 7 | @statusBarTile?.destroy() 8 | @statusBarTile = null 9 | 10 | providePlatformIOIDETerminal: -> 11 | updateProcessEnv: (variables) -> 12 | for name, value of variables 13 | process.env[name] = value 14 | run: (commands) => 15 | @statusBarTile.runCommandInNewTerminal commands 16 | getTerminalViews: () => 17 | @statusBarTile.terminalViews 18 | open: () => 19 | @statusBarTile.runNewTerminal() 20 | 21 | provideRunInTerminal: -> 22 | run: (commands) => 23 | @statusBarTile.runCommandInNewTerminal commands 24 | getTerminalViews: () => 25 | @statusBarTile.terminalViews 26 | 27 | consumeStatusBar: (statusBarProvider) -> 28 | @statusBarTile = new (require './status-bar')(statusBarProvider) 29 | 30 | config: 31 | toggles: 32 | type: 'object' 33 | order: 1 34 | properties: 35 | autoClose: 36 | title: 'Close Terminal on Exit' 37 | description: 'Should the terminal close if the shell exits?' 38 | type: 'boolean' 39 | default: false 40 | cursorBlink: 41 | title: 'Cursor Blink' 42 | description: 'Should the cursor blink when the terminal is active?' 43 | type: 'boolean' 44 | default: true 45 | runInsertedText: 46 | title: 'Run Inserted Text' 47 | description: 'Run text inserted via `platformio-ide-terminal:insert-text` as a command? **This will append an end-of-line character to input.**' 48 | type: 'boolean' 49 | default: true 50 | selectToCopy: 51 | title: 'Select To Copy' 52 | description: 'Copies text to clipboard when selection happens.' 53 | type: 'boolean' 54 | default: true 55 | loginShell: 56 | title: 'Login Shell' 57 | description: 'Use --login on zsh and bash.' 58 | type: 'boolean' 59 | default: true 60 | showToolbar: 61 | title: 'Show Toolbar' 62 | description: 'Show toolbar above terminal window.' 63 | type: 'boolean' 64 | default: true 65 | core: 66 | type: 'object' 67 | order: 2 68 | properties: 69 | autoRunCommand: 70 | title: 'Auto Run Command' 71 | description: 'Command to run on terminal initialization.' 72 | type: 'string' 73 | default: '' 74 | mapTerminalsTo: 75 | title: 'Map Terminals To' 76 | description: 'Map terminals to each file or folder. Default is no action or mapping at all. **Restart required.**' 77 | type: 'string' 78 | default: 'None' 79 | enum: ['None', 'File', 'Folder'] 80 | mapTerminalsToAutoOpen: 81 | title: 'Auto Open a New Terminal (For Terminal Mapping)' 82 | description: 'Should a new terminal be opened for new items? **Note:** This works in conjunction with `Map Terminals To` above.' 83 | type: 'boolean' 84 | default: false 85 | scrollback: 86 | title: 'Scroll Back' 87 | description: 'How many lines of history should be kept?' 88 | type: 'integer' 89 | default: 1000 90 | shell: 91 | title: 'Shell Override' 92 | description: 'Override the default shell instance to launch.' 93 | type: 'string' 94 | default: do -> 95 | if process.platform is 'win32' 96 | path = require 'path' 97 | path.resolve(process.env.SystemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe') 98 | else 99 | process.env.SHELL || '/bin/bash' 100 | shellArguments: 101 | title: 'Shell Arguments' 102 | description: 'Specify some arguments to use when launching the shell.' 103 | type: 'string' 104 | default: '' 105 | shellEnv: 106 | title: 'Shell Environment Variables' 107 | description: 'Specify some additional environment variables, space separated with the form `VAR=VALUE`' 108 | type: 'string' 109 | default: '' 110 | workingDirectory: 111 | title: 'Working Directory' 112 | description: 'Which directory should be the present working directory when a new terminal is made?' 113 | type: 'string' 114 | default: 'Project' 115 | enum: ['Home', 'Project', 'Active File'] 116 | style: 117 | type: 'object' 118 | order: 3 119 | properties: 120 | animationSpeed: 121 | title: 'Animation Speed' 122 | description: 'How fast should the window animate?' 123 | type: 'number' 124 | default: '1' 125 | minimum: '0' 126 | maximum: '100' 127 | fontFamily: 128 | title: 'Font Family' 129 | description: 'Override the terminal\'s default font family. **You must use a [monospaced font](https://en.wikipedia.org/wiki/List_of_typefaces#Monospace)!**' 130 | type: 'string' 131 | default: '' 132 | fontSize: 133 | title: 'Font Size' 134 | description: 'Override the terminal\'s default font size.' 135 | type: 'string' 136 | default: '' 137 | defaultPanelHeight: 138 | title: 'Default Panel Height' 139 | description: 'Default height of a terminal panel. **You may enter a value in px, em, or %.**' 140 | type: 'string' 141 | default: '300px' 142 | theme: 143 | title: 'Theme' 144 | description: 'Select a theme for the terminal.' 145 | type: 'string' 146 | default: 'standard' 147 | enum: [ 148 | 'standard', 149 | 'inverse', 150 | 'linux', 151 | 'grass', 152 | 'homebrew', 153 | 'man-page', 154 | 'novel', 155 | 'ocean', 156 | 'pro', 157 | 'red', 158 | 'red-sands', 159 | 'silver-aerogel', 160 | 'solarized-dark', 161 | 'solid-colors', 162 | 'dracula', 163 | 'one-dark', 164 | 'christmas', 165 | 'predawn', 166 | 'city-lights', 167 | 'solarized-light' 168 | ] 169 | ansiColors: 170 | type: 'object' 171 | order: 4 172 | properties: 173 | normal: 174 | type: 'object' 175 | order: 1 176 | properties: 177 | black: 178 | title: 'Black' 179 | description: 'Black color used for terminal ANSI color set.' 180 | type: 'color' 181 | default: '#000000' 182 | red: 183 | title: 'Red' 184 | description: 'Red color used for terminal ANSI color set.' 185 | type: 'color' 186 | default: '#CD0000' 187 | green: 188 | title: 'Green' 189 | description: 'Green color used for terminal ANSI color set.' 190 | type: 'color' 191 | default: '#00CD00' 192 | yellow: 193 | title: 'Yellow' 194 | description: 'Yellow color used for terminal ANSI color set.' 195 | type: 'color' 196 | default: '#CDCD00' 197 | blue: 198 | title: 'Blue' 199 | description: 'Blue color used for terminal ANSI color set.' 200 | type: 'color' 201 | default: '#0000CD' 202 | magenta: 203 | title: 'Magenta' 204 | description: 'Magenta color used for terminal ANSI color set.' 205 | type: 'color' 206 | default: '#CD00CD' 207 | cyan: 208 | title: 'Cyan' 209 | description: 'Cyan color used for terminal ANSI color set.' 210 | type: 'color' 211 | default: '#00CDCD' 212 | white: 213 | title: 'White' 214 | description: 'White color used for terminal ANSI color set.' 215 | type: 'color' 216 | default: '#E5E5E5' 217 | zBright: 218 | type: 'object' 219 | order: 2 220 | properties: 221 | brightBlack: 222 | title: 'Bright Black' 223 | description: 'Bright black color used for terminal ANSI color set.' 224 | type: 'color' 225 | default: '#7F7F7F' 226 | brightRed: 227 | title: 'Bright Red' 228 | description: 'Bright red color used for terminal ANSI color set.' 229 | type: 'color' 230 | default: '#FF0000' 231 | brightGreen: 232 | title: 'Bright Green' 233 | description: 'Bright green color used for terminal ANSI color set.' 234 | type: 'color' 235 | default: '#00FF00' 236 | brightYellow: 237 | title: 'Bright Yellow' 238 | description: 'Bright yellow color used for terminal ANSI color set.' 239 | type: 'color' 240 | default: '#FFFF00' 241 | brightBlue: 242 | title: 'Bright Blue' 243 | description: 'Bright blue color used for terminal ANSI color set.' 244 | type: 'color' 245 | default: '#0000FF' 246 | brightMagenta: 247 | title: 'Bright Magenta' 248 | description: 'Bright magenta color used for terminal ANSI color set.' 249 | type: 'color' 250 | default: '#FF00FF' 251 | brightCyan: 252 | title: 'Bright Cyan' 253 | description: 'Bright cyan color used for terminal ANSI color set.' 254 | type: 'color' 255 | default: '#00FFFF' 256 | brightWhite: 257 | title: 'Bright White' 258 | description: 'Bright white color used for terminal ANSI color set.' 259 | type: 'color' 260 | default: '#FFFFFF' 261 | iconColors: 262 | type: 'object' 263 | order: 5 264 | properties: 265 | red: 266 | title: 'Status Icon Red' 267 | description: 'Red color used for status icon.' 268 | type: 'color' 269 | default: 'red' 270 | orange: 271 | title: 'Status Icon Orange' 272 | description: 'Orange color used for status icon.' 273 | type: 'color' 274 | default: 'orange' 275 | yellow: 276 | title: 'Status Icon Yellow' 277 | description: 'Yellow color used for status icon.' 278 | type: 'color' 279 | default: 'yellow' 280 | green: 281 | title: 'Status Icon Green' 282 | description: 'Green color used for status icon.' 283 | type: 'color' 284 | default: 'green' 285 | blue: 286 | title: 'Status Icon Blue' 287 | description: 'Blue color used for status icon.' 288 | type: 'color' 289 | default: 'blue' 290 | purple: 291 | title: 'Status Icon Purple' 292 | description: 'Purple color used for status icon.' 293 | type: 'color' 294 | default: 'purple' 295 | pink: 296 | title: 'Status Icon Pink' 297 | description: 'Pink color used for status icon.' 298 | type: 'color' 299 | default: 'hotpink' 300 | cyan: 301 | title: 'Status Icon Cyan' 302 | description: 'Cyan color used for status icon.' 303 | type: 'color' 304 | default: 'cyan' 305 | magenta: 306 | title: 'Status Icon Magenta' 307 | description: 'Magenta color used for status icon.' 308 | type: 'color' 309 | default: 'magenta' 310 | customTexts: 311 | type: 'object' 312 | order: 6 313 | properties: 314 | customText1: 315 | title: 'Custom text 1' 316 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-1, $S is replaced by selection, $F is replaced by file name, $D is replaced by file directory, $L is replaced by line number of cursor, $$ is replaced by $' 317 | type: 'string' 318 | default: '' 319 | customText2: 320 | title: 'Custom text 2' 321 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-2' 322 | type: 'string' 323 | default: '' 324 | customText3: 325 | title: 'Custom text 3' 326 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-3' 327 | type: 'string' 328 | default: '' 329 | customText4: 330 | title: 'Custom text 4' 331 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-4' 332 | type: 'string' 333 | default: '' 334 | customText5: 335 | title: 'Custom text 5' 336 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-5' 337 | type: 'string' 338 | default: '' 339 | customText6: 340 | title: 'Custom text 6' 341 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-6' 342 | type: 'string' 343 | default: '' 344 | customText7: 345 | title: 'Custom text 7' 346 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-7' 347 | type: 'string' 348 | default: '' 349 | customText8: 350 | title: 'Custom text 8' 351 | description: 'Text to paste when calling platformio-ide-terminal:insert-custom-text-8' 352 | type: 'string' 353 | default: '' 354 | -------------------------------------------------------------------------------- /lib/process.coffee: -------------------------------------------------------------------------------- 1 | pty = require 'node-pty-prebuilt-multiarch' 2 | path = require 'path' 3 | fs = require 'fs' 4 | _ = require 'underscore' 5 | child = require 'child_process' 6 | 7 | systemLanguage = do -> 8 | language = "en_US.UTF-8" 9 | if process.platform is 'darwin' 10 | try 11 | command = 'plutil -convert json -o - ~/Library/Preferences/.GlobalPreferences.plist' 12 | language = "#{JSON.parse(child.execSync(command).toString()).AppleLocale}.UTF-8" 13 | return language 14 | 15 | filteredEnvironment = do -> 16 | env = _.omit process.env, 'ATOM_HOME', 'ELECTRON_RUN_AS_NODE', 'GOOGLE_API_KEY', 'NODE_ENV', 'NODE_PATH', 'userAgent', 'taskPath' 17 | env.LANG ?= systemLanguage 18 | env.TERM_PROGRAM = 'platformio-ide-terminal' 19 | return env 20 | 21 | module.exports = (pwd, shell, args, env, options={}) -> 22 | callback = @async() 23 | 24 | if shell 25 | ptyProcess = pty.fork shell, args, 26 | cwd: pwd, 27 | env: _.extend(filteredEnvironment, env), 28 | name: 'xterm-256color' 29 | 30 | title = shell = path.basename shell 31 | else 32 | ptyProcess = pty.open() 33 | 34 | emitTitle = _.throttle -> 35 | emit('platformio-ide-terminal:title', ptyProcess.process) 36 | , 500, true 37 | 38 | ptyProcess.on 'data', (data) -> 39 | emit('platformio-ide-terminal:data', data) 40 | emitTitle() 41 | 42 | ptyProcess.on 'exit', -> 43 | emit('platformio-ide-terminal:exit') 44 | callback() 45 | 46 | process.on 'message', ({event, cols, rows, text}={}) -> 47 | switch event 48 | when 'resize' then ptyProcess.resize(cols, rows) 49 | when 'input' then ptyProcess.write(text) 50 | when 'pty' then emit('platformio-ide-terminal:pty', ptyProcess.pty) 51 | -------------------------------------------------------------------------------- /lib/rename-dialog.coffee: -------------------------------------------------------------------------------- 1 | Dialog = require "./dialog" 2 | 3 | module.exports = 4 | class RenameDialog extends Dialog 5 | constructor: (@statusIcon) -> 6 | super 7 | prompt: "Rename" 8 | iconClass: "icon-pencil" 9 | placeholderText: @statusIcon.getName() 10 | 11 | onConfirm: (newTitle) -> 12 | @statusIcon.updateName newTitle.trim() 13 | @cancel() 14 | -------------------------------------------------------------------------------- /lib/status-bar.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | {$, View} = require 'atom-space-pen-views' 3 | 4 | PlatformIOTerminalView = require './view' 5 | StatusIcon = require './status-icon' 6 | 7 | os = require 'os' 8 | path = require 'path' 9 | _ = require 'underscore' 10 | 11 | module.exports = 12 | class StatusBar extends View 13 | terminalViews: [] 14 | activeTerminal: null 15 | returnFocus: null 16 | 17 | @content: -> 18 | @div class: 'platformio-ide-terminal status-bar', tabindex: -1, => 19 | @i class: "icon icon-plus", click: 'newTerminalView', outlet: 'plusBtn' 20 | @ul class: "list-inline status-container", tabindex: '-1', outlet: 'statusContainer', is: 'space-pen-ul' 21 | @i class: "icon icon-x", click: 'closeAll', outlet: 'closeBtn' 22 | 23 | initialize: (@statusBarProvider) -> 24 | @subscriptions = new CompositeDisposable() 25 | 26 | @subscriptions.add atom.commands.add 'atom-workspace', 27 | 'platformio-ide-terminal:focus': => @focusTerminal() 28 | 'platformio-ide-terminal:new': => @newTerminalView() 29 | 'platformio-ide-terminal:toggle': => @toggle() 30 | 'platformio-ide-terminal:next': => 31 | return unless @activeTerminal 32 | return if @activeTerminal.isAnimating() 33 | @activeTerminal.open() if @activeNextTerminalView() 34 | 'platformio-ide-terminal:prev': => 35 | return unless @activeTerminal 36 | return if @activeTerminal.isAnimating() 37 | @activeTerminal.open() if @activePrevTerminalView() 38 | 'platformio-ide-terminal:clear': => @clear() 39 | 'platformio-ide-terminal:close': => @destroyActiveTerm() 40 | 'platformio-ide-terminal:close-all': => @closeAll() 41 | 'platformio-ide-terminal:rename': => @runInActiveView (i) -> i.rename() 42 | 'platformio-ide-terminal:insert-selected-text': => @runInActiveView (i) -> i.insertSelection('$S') 43 | 'platformio-ide-terminal:insert-text': => @runInActiveView (i) -> i.inputDialog() 44 | 'platformio-ide-terminal:insert-custom-text-1': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText1')) 45 | 'platformio-ide-terminal:insert-custom-text-2': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText2')) 46 | 'platformio-ide-terminal:insert-custom-text-3': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText3')) 47 | 'platformio-ide-terminal:insert-custom-text-4': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText4')) 48 | 'platformio-ide-terminal:insert-custom-text-5': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText5')) 49 | 'platformio-ide-terminal:insert-custom-text-6': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText6')) 50 | 'platformio-ide-terminal:insert-custom-text-7': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText7')) 51 | 'platformio-ide-terminal:insert-custom-text-8': => @runInActiveView (i) -> i.insertSelection(atom.config.get('platformio-ide-terminal.customTexts.customText8')) 52 | 'platformio-ide-terminal:fullscreen': => @activeTerminal.maximize() 53 | 54 | @subscriptions.add atom.commands.add '.xterm', 55 | 'platformio-ide-terminal:paste': => @runInActiveView (i) -> i.paste() 56 | 'platformio-ide-terminal:copy': => @runInActiveView (i) -> i.copy() 57 | 58 | @subscriptions.add atom.workspace.onDidChangeActivePaneItem (item) => 59 | return unless item? 60 | 61 | if item.constructor.name is "PlatformIOTerminalView" 62 | setTimeout item.focus, 100 63 | else if item.constructor.name is "TextEditor" 64 | mapping = atom.config.get('platformio-ide-terminal.core.mapTerminalsTo') 65 | return if mapping is 'None' 66 | return unless item.getPath() 67 | 68 | switch mapping 69 | when 'File' 70 | nextTerminal = @getTerminalById item.getPath(), (view) -> view.getId().filePath 71 | when 'Folder' 72 | nextTerminal = @getTerminalById path.dirname(item.getPath()), (view) -> view.getId().folderPath 73 | 74 | prevTerminal = @getActiveTerminalView() 75 | if prevTerminal != nextTerminal 76 | if not nextTerminal? 77 | if atom.config.get('platformio-ide-terminal.core.mapTerminalsToAutoOpen') 78 | nextTerminal = @createTerminalView() 79 | else 80 | @setActiveTerminalView(nextTerminal) 81 | nextTerminal.toggle() if prevTerminal?.panel.isVisible() 82 | 83 | @registerContextMenu() 84 | 85 | @subscriptions.add atom.tooltips.add @plusBtn, title: 'New Terminal' 86 | @subscriptions.add atom.tooltips.add @closeBtn, title: 'Close All' 87 | 88 | @statusContainer.on 'dblclick', (event) => 89 | @newTerminalView() unless event.target != event.delegateTarget 90 | 91 | @statusContainer.on 'dragstart', '.pio-terminal-status-icon', @onDragStart 92 | @statusContainer.on 'dragend', '.pio-terminal-status-icon', @onDragEnd 93 | @statusContainer.on 'dragleave', @onDragLeave 94 | @statusContainer.on 'dragover', @onDragOver 95 | @statusContainer.on 'drop', @onDrop 96 | 97 | handleBlur = => 98 | if terminal = PlatformIOTerminalView.getFocusedTerminal() 99 | @returnFocus = @terminalViewForTerminal(terminal) 100 | terminal.blur() 101 | 102 | handleFocus = => 103 | if @returnFocus 104 | setTimeout => 105 | @returnFocus?.focus(true) 106 | @returnFocus = null 107 | , 100 108 | 109 | window.addEventListener 'blur', handleBlur 110 | @subscriptions.add dispose: -> 111 | window.removeEventListener 'blur', handleBlur 112 | 113 | window.addEventListener 'focus', handleFocus 114 | @subscriptions.add dispose: -> 115 | window.removeEventListener 'focus', handleFocus 116 | 117 | @attach() 118 | 119 | registerContextMenu: -> 120 | @subscriptions.add atom.commands.add '.platformio-ide-terminal.status-bar', 121 | 'platformio-ide-terminal:status-red': @setStatusColor 122 | 'platformio-ide-terminal:status-orange': @setStatusColor 123 | 'platformio-ide-terminal:status-yellow': @setStatusColor 124 | 'platformio-ide-terminal:status-green': @setStatusColor 125 | 'platformio-ide-terminal:status-blue': @setStatusColor 126 | 'platformio-ide-terminal:status-purple': @setStatusColor 127 | 'platformio-ide-terminal:status-pink': @setStatusColor 128 | 'platformio-ide-terminal:status-cyan': @setStatusColor 129 | 'platformio-ide-terminal:status-magenta': @setStatusColor 130 | 'platformio-ide-terminal:status-default': @clearStatusColor 131 | 'platformio-ide-terminal:context-close': (event) -> 132 | $(event.target).closest('.pio-terminal-status-icon')[0].terminalView.destroy() 133 | 'platformio-ide-terminal:context-hide': (event) -> 134 | statusIcon = $(event.target).closest('.pio-terminal-status-icon')[0] 135 | statusIcon.terminalView.hide() if statusIcon.isActive() 136 | 'platformio-ide-terminal:context-rename': (event) -> 137 | $(event.target).closest('.pio-terminal-status-icon')[0].rename() 138 | 139 | registerPaneSubscription: -> 140 | @subscriptions.add @paneSubscription = atom.workspace.observePanes (pane) => 141 | paneElement = $(atom.views.getView(pane)) 142 | tabBar = paneElement.find('ul') 143 | 144 | tabBar.on 'drop', (event) => @onDropTabBar(event, pane) 145 | tabBar.on 'dragstart', (event) -> 146 | return unless event.target.item?.constructor.name is 'PlatformIOTerminalView' 147 | event.originalEvent.dataTransfer.setData 'platformio-ide-terminal-tab', 'true' 148 | pane.onDidDestroy -> tabBar.off 'drop', @onDropTabBar 149 | 150 | createTerminalView: (autoRun) -> 151 | shell = atom.config.get 'platformio-ide-terminal.core.shell' 152 | shellArguments = atom.config.get 'platformio-ide-terminal.core.shellArguments' 153 | args = shellArguments.split(/\s+/g).filter (arg) -> arg 154 | shellEnv = atom.config.get 'platformio-ide-terminal.core.shellEnv' 155 | env = {} 156 | shellEnv.split(' ').forEach((element) => 157 | configVar = element.split('=') 158 | envVar = {} 159 | envVar[configVar[0]] = configVar[1] 160 | env = _.extend(env, envVar) 161 | ) 162 | @createEmptyTerminalView autoRun, shell, args, env 163 | 164 | createEmptyTerminalView: (autoRun=[], shell = null, args = [], env= {}) -> 165 | @registerPaneSubscription() unless @paneSubscription? 166 | 167 | projectFolder = atom.project.getPaths()[0] 168 | editorPath = atom.workspace.getActiveTextEditor()?.getPath() 169 | 170 | if editorPath? 171 | editorFolder = path.dirname(editorPath) 172 | for directory in atom.project.getPaths() 173 | if editorPath.indexOf(directory) >= 0 174 | projectFolder = directory 175 | 176 | projectFolder = undefined if projectFolder?.indexOf('atom://') >= 0 177 | 178 | home = if process.platform is 'win32' then process.env.HOMEPATH else process.env.HOME 179 | 180 | switch atom.config.get('platformio-ide-terminal.core.workingDirectory') 181 | when 'Project' then pwd = projectFolder or editorFolder or home 182 | when 'Active File' then pwd = editorFolder or projectFolder or home 183 | else pwd = home 184 | 185 | id = editorPath or projectFolder or home 186 | id = filePath: id, folderPath: path.dirname(id) 187 | 188 | statusIcon = new StatusIcon() 189 | platformIOTerminalView = new PlatformIOTerminalView(id, pwd, statusIcon, this, shell, args, env, autoRun) 190 | statusIcon.initialize(platformIOTerminalView) 191 | 192 | platformIOTerminalView.attach() 193 | 194 | @terminalViews.push platformIOTerminalView 195 | @statusContainer.append statusIcon 196 | return platformIOTerminalView 197 | 198 | activeNextTerminalView: -> 199 | index = @indexOf(@activeTerminal) 200 | return false if index < 0 201 | @activeTerminalView index + 1 202 | 203 | activePrevTerminalView: -> 204 | index = @indexOf(@activeTerminal) 205 | return false if index < 0 206 | @activeTerminalView index - 1 207 | 208 | indexOf: (view) -> 209 | @terminalViews.indexOf(view) 210 | 211 | activeTerminalView: (index) -> 212 | return false if @terminalViews.length < 2 213 | 214 | if index >= @terminalViews.length 215 | index = 0 216 | if index < 0 217 | index = @terminalViews.length - 1 218 | 219 | @activeTerminal = @terminalViews[index] 220 | return true 221 | 222 | getActiveTerminalView: -> 223 | return @activeTerminal 224 | 225 | focusTerminal: -> 226 | return unless @activeTerminal? 227 | 228 | if terminal = PlatformIOTerminalView.getFocusedTerminal() 229 | @activeTerminal.blur() 230 | else 231 | @activeTerminal.focusTerminal() 232 | 233 | getTerminalById: (target, selector) -> 234 | selector ?= (terminal) -> terminal.id 235 | 236 | for index in [0 .. @terminalViews.length] 237 | terminal = @terminalViews[index] 238 | if terminal? 239 | return terminal if selector(terminal) == target 240 | 241 | return null 242 | 243 | terminalViewForTerminal: (terminal) -> 244 | for index in [0 .. @terminalViews.length] 245 | terminalView = @terminalViews[index] 246 | if terminalView? 247 | return terminalView if terminalView.getTerminal() == terminal 248 | 249 | return null 250 | 251 | runInActiveView: (callback) -> 252 | view = @getActiveTerminalView() 253 | if view? 254 | return callback(view) 255 | return null 256 | 257 | runNewTerminal: () -> 258 | @activeTerminal = @createEmptyTerminalView() 259 | @activeTerminal.toggle() 260 | return @activeTerminal 261 | 262 | runCommandInNewTerminal: (commands) -> 263 | @activeTerminal = @createTerminalView(commands) 264 | @activeTerminal.toggle() 265 | 266 | runInOpenView: (callback) -> 267 | view = @getActiveTerminalView() 268 | if view? and view.panel.isVisible() 269 | return callback(view) 270 | return null 271 | 272 | setActiveTerminalView: (view) -> 273 | @activeTerminal = view 274 | 275 | removeTerminalView: (view) -> 276 | index = @indexOf view 277 | return if index < 0 278 | @terminalViews.splice index, 1 279 | 280 | @activateAdjacentTerminal index 281 | 282 | activateAdjacentTerminal: (index=0) -> 283 | return false unless @terminalViews.length > 0 284 | 285 | index = Math.max(0, index - 1) 286 | @activeTerminal = @terminalViews[index] 287 | 288 | return true 289 | 290 | newTerminalView: -> 291 | return if @activeTerminal?.animating 292 | 293 | @activeTerminal = @createTerminalView() 294 | @activeTerminal.toggle() 295 | 296 | attach: -> 297 | @statusBarProvider.addLeftTile(item: this, priority: -93) 298 | 299 | destroyActiveTerm: -> 300 | return unless @activeTerminal? 301 | 302 | index = @indexOf(@activeTerminal) 303 | @activeTerminal.destroy() 304 | @activeTerminal = null 305 | 306 | @activateAdjacentTerminal index 307 | 308 | closeAll: => 309 | for index in [@terminalViews.length .. 0] 310 | view = @terminalViews[index] 311 | if view? 312 | view.destroy() 313 | @activeTerminal = null 314 | 315 | destroy: -> 316 | @subscriptions.dispose() 317 | for view in @terminalViews 318 | view.ptyProcess.terminate() 319 | view.terminal.destroy() 320 | @detach() 321 | 322 | toggle: -> 323 | if @terminalViews.length == 0 324 | @activeTerminal = @createTerminalView() 325 | else if @activeTerminal == null 326 | @activeTerminal = @terminalViews[0] 327 | @activeTerminal.toggle() 328 | 329 | clear: -> 330 | @destroyActiveTerm() 331 | @newTerminalView() 332 | 333 | setStatusColor: (event) -> 334 | color = event.type.match(/\w+$/)[0] 335 | color = atom.config.get("platformio-ide-terminal.iconColors.#{color}").toRGBAString() 336 | $(event.target).closest('.pio-terminal-status-icon').css 'color', color 337 | 338 | clearStatusColor: (event) -> 339 | $(event.target).closest('.pio-terminal-status-icon').css 'color', '' 340 | 341 | onDragStart: (event) => 342 | event.originalEvent.dataTransfer.setData 'platformio-ide-terminal-panel', 'true' 343 | 344 | element = $(event.target).closest('.pio-terminal-status-icon') 345 | element.addClass 'is-dragging' 346 | event.originalEvent.dataTransfer.setData 'from-index', element.index() 347 | 348 | onDragLeave: (event) => 349 | @removePlaceholder() 350 | 351 | onDragEnd: (event) => 352 | @clearDropTarget() 353 | 354 | onDragOver: (event) => 355 | event.preventDefault() 356 | event.stopPropagation() 357 | unless event.originalEvent.dataTransfer.getData('platformio-ide-terminal') is 'true' 358 | return 359 | 360 | newDropTargetIndex = @getDropTargetIndex(event) 361 | return unless newDropTargetIndex? 362 | @removeDropTargetClasses() 363 | statusIcons = @statusContainer.children '.pio-terminal-status-icon' 364 | 365 | if newDropTargetIndex < statusIcons.length 366 | element = statusIcons.eq(newDropTargetIndex).addClass 'is-drop-target' 367 | @getPlaceholder().insertBefore(element) 368 | else 369 | element = statusIcons.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after' 370 | @getPlaceholder().insertAfter(element) 371 | 372 | onDrop: (event) => 373 | {dataTransfer} = event.originalEvent 374 | panelEvent = dataTransfer.getData('platformio-ide-terminal-panel') is 'true' 375 | tabEvent = dataTransfer.getData('platformio-ide-terminal-tab') is 'true' 376 | return unless panelEvent or tabEvent 377 | 378 | event.preventDefault() 379 | event.stopPropagation() 380 | 381 | toIndex = @getDropTargetIndex(event) 382 | @clearDropTarget() 383 | 384 | if tabEvent 385 | fromIndex = parseInt(dataTransfer.getData('sortable-index')) 386 | paneIndex = parseInt(dataTransfer.getData('from-pane-index')) 387 | pane = atom.workspace.getPanes()[paneIndex] 388 | view = pane.itemAtIndex(fromIndex) 389 | pane.removeItem(view, false) 390 | view.show() 391 | 392 | view.toggleTabView() 393 | @terminalViews.push view 394 | view.open() if view.statusIcon.isActive() 395 | @statusContainer.append view.statusIcon 396 | fromIndex = @terminalViews.length - 1 397 | else 398 | fromIndex = parseInt(dataTransfer.getData('from-index')) 399 | @updateOrder(fromIndex, toIndex) 400 | 401 | onDropTabBar: (event, pane) => 402 | {dataTransfer} = event.originalEvent 403 | return unless dataTransfer.getData('platformio-ide-terminal-panel') is 'true' 404 | 405 | event.preventDefault() 406 | event.stopPropagation() 407 | @clearDropTarget() 408 | 409 | fromIndex = parseInt(dataTransfer.getData('from-index')) 410 | view = @terminalViews[fromIndex] 411 | view.css "height", "" 412 | view.terminal.element.style.height = "" 413 | tabBar = $(event.target).closest('.tab-bar') 414 | 415 | view.toggleTabView() 416 | @removeTerminalView view 417 | @statusContainer.children().eq(fromIndex).detach() 418 | view.statusIcon.removeTooltip() 419 | 420 | pane.addItem view, pane.getItems().length 421 | pane.activateItem view 422 | 423 | view.focus() 424 | 425 | clearDropTarget: -> 426 | element = @find('.is-dragging') 427 | element.removeClass 'is-dragging' 428 | @removeDropTargetClasses() 429 | @removePlaceholder() 430 | 431 | removeDropTargetClasses: -> 432 | @statusContainer.find('.is-drop-target').removeClass 'is-drop-target' 433 | @statusContainer.find('.drop-target-is-after').removeClass 'drop-target-is-after' 434 | 435 | getDropTargetIndex: (event) -> 436 | target = $(event.target) 437 | return if @isPlaceholder(target) 438 | 439 | statusIcons = @statusContainer.children('.pio-terminal-status-icon') 440 | element = target.closest('.pio-terminal-status-icon') 441 | element = statusIcons.last() if element.length is 0 442 | 443 | return 0 unless element.length 444 | 445 | elementCenter = element.offset().left + element.width() / 2 446 | 447 | if event.originalEvent.pageX < elementCenter 448 | statusIcons.index(element) 449 | else if element.next('.pio-terminal-status-icon').length > 0 450 | statusIcons.index(element.next('.pio-terminal-status-icon')) 451 | else 452 | statusIcons.index(element) + 1 453 | 454 | getPlaceholder: -> 455 | @placeholderEl ?= $('
  • ') 456 | 457 | removePlaceholder: -> 458 | @placeholderEl?.remove() 459 | @placeholderEl = null 460 | 461 | isPlaceholder: (element) -> 462 | element.is('.placeholder') 463 | 464 | iconAtIndex: (index) -> 465 | @getStatusIcons().eq(index) 466 | 467 | getStatusIcons: -> 468 | @statusContainer.children('.pio-terminal-status-icon') 469 | 470 | moveIconToIndex: (icon, toIndex) -> 471 | followingIcon = @getStatusIcons()[toIndex] 472 | container = @statusContainer[0] 473 | if followingIcon? 474 | container.insertBefore(icon, followingIcon) 475 | else 476 | container.appendChild(icon) 477 | 478 | moveTerminalView: (fromIndex, toIndex) => 479 | activeTerminal = @getActiveTerminalView() 480 | view = @terminalViews.splice(fromIndex, 1)[0] 481 | @terminalViews.splice toIndex, 0, view 482 | @setActiveTerminalView activeTerminal 483 | 484 | updateOrder: (fromIndex, toIndex) -> 485 | return if fromIndex is toIndex 486 | toIndex-- if fromIndex < toIndex 487 | 488 | icon = @getStatusIcons().eq(fromIndex).detach() 489 | @moveIconToIndex icon.get(0), toIndex 490 | @moveTerminalView fromIndex, toIndex 491 | icon.addClass 'inserted' 492 | icon.one 'webkitAnimationEnd', -> icon.removeClass('inserted') 493 | -------------------------------------------------------------------------------- /lib/status-icon.coffee: -------------------------------------------------------------------------------- 1 | {CompositeDisposable} = require 'atom' 2 | 3 | RenameDialog = null 4 | 5 | module.exports = 6 | class StatusIcon extends HTMLElement 7 | active: false 8 | 9 | initialize: (@terminalView) -> 10 | @classList.add 'pio-terminal-status-icon' 11 | 12 | @icon = document.createElement('i') 13 | @icon.classList.add 'icon', 'icon-terminal' 14 | @appendChild(@icon) 15 | 16 | @name = document.createElement('span') 17 | @name.classList.add 'name' 18 | @appendChild(@name) 19 | 20 | @dataset.type = @terminalView.constructor?.name 21 | 22 | @addEventListener 'click', ({which, ctrlKey}) => 23 | if which is 1 24 | @terminalView.toggle() 25 | true 26 | else if which is 2 27 | @terminalView.destroy() 28 | false 29 | 30 | @setupTooltip() 31 | 32 | setupTooltip: -> 33 | 34 | onMouseEnter = (event) => 35 | return if event.detail is 'platformio-ide-terminal' 36 | @updateTooltip() 37 | 38 | @mouseEnterSubscription = dispose: => 39 | @removeEventListener('mouseenter', onMouseEnter) 40 | @mouseEnterSubscription = null 41 | 42 | @addEventListener('mouseenter', onMouseEnter) 43 | 44 | updateTooltip: -> 45 | @removeTooltip() 46 | 47 | if process = @terminalView.getTerminalTitle() 48 | @tooltip = atom.tooltips.add this, 49 | title: process 50 | html: false 51 | delay: 52 | show: 1000 53 | hide: 100 54 | 55 | @dispatchEvent(new CustomEvent('mouseenter', bubbles: true, detail: 'platformio-ide-terminal')) 56 | 57 | removeTooltip: -> 58 | @tooltip.dispose() if @tooltip 59 | @tooltip = null 60 | 61 | destroy: -> 62 | @removeTooltip() 63 | @mouseEnterSubscription.dispose() if @mouseEnterSubscription 64 | @remove() 65 | 66 | activate: -> 67 | @classList.add 'active' 68 | @active = true 69 | 70 | isActive: -> 71 | @classList.contains 'active' 72 | 73 | deactivate: -> 74 | @classList.remove 'active' 75 | @active = false 76 | 77 | toggle: -> 78 | if @active 79 | @classList.remove 'active' 80 | else 81 | @classList.add 'active' 82 | @active = !@active 83 | 84 | isActive: -> 85 | return @active 86 | 87 | rename: -> 88 | RenameDialog ?= require './rename-dialog' 89 | dialog = new RenameDialog this 90 | dialog.attach() 91 | 92 | getName: -> @name.textContent.substring(1) 93 | 94 | updateName: (name) -> 95 | if name isnt @getName() 96 | name = " " + name if name 97 | @name.innerHTML = name 98 | @terminalView.emit 'did-change-title' 99 | 100 | module.exports = document.registerElement('pio-terminal-status-icon', prototype: StatusIcon.prototype, extends: 'li') 101 | -------------------------------------------------------------------------------- /lib/view.coffee: -------------------------------------------------------------------------------- 1 | {Task, CompositeDisposable, Emitter} = require 'atom' 2 | {$, View} = require 'atom-space-pen-views' 3 | 4 | Pty = require.resolve './process' 5 | Terminal = require 'term.js' 6 | InputDialog = null 7 | 8 | path = require 'path' 9 | os = require 'os' 10 | 11 | lastOpenedView = null 12 | lastActiveElement = null 13 | 14 | module.exports = 15 | class PlatformIOTerminalView extends View 16 | animating: false 17 | id: '' 18 | maximized: false 19 | opened: false 20 | pwd: '' 21 | windowHeight: $(window).height() 22 | rowHeight: 20 23 | shell: '' 24 | tabView: false 25 | 26 | @content: -> 27 | @div class: 'platformio-ide-terminal terminal-view', outlet: 'platformIOTerminalView', => 28 | @div class: 'panel-divider', outlet: 'panelDivider' 29 | @section class: 'input-block', => 30 | @div outlet: 'toolbar', class: 'btn-toolbar', => 31 | @div class: 'btn-group', => 32 | @button outlet: 'inputBtn', class: 'btn icon icon-keyboard', click: 'inputDialog' 33 | @div class: 'btn-group right', => 34 | @button outlet: 'hideBtn', class: 'btn icon icon-chevron-down', click: 'hide' 35 | @button outlet: 'maximizeBtn', class: 'btn icon icon-screen-full', click: 'maximize' 36 | @button outlet: 'closeBtn', class: 'btn icon icon-x', click: 'destroy' 37 | @div class: 'xterm', outlet: 'xterm' 38 | 39 | @getFocusedTerminal: -> 40 | return Terminal.Terminal.focus 41 | 42 | initialize: (@id, @pwd, @statusIcon, @statusBar, @shell, @args=[], @env={}, @autoRun=[]) -> 43 | @subscriptions = new CompositeDisposable 44 | @emitter = new Emitter 45 | 46 | @subscriptions.add atom.tooltips.add @closeBtn, 47 | title: 'Close' 48 | @subscriptions.add atom.tooltips.add @hideBtn, 49 | title: 'Hide' 50 | @subscriptions.add @maximizeBtn.tooltip = atom.tooltips.add @maximizeBtn, 51 | title: 'Fullscreen' 52 | @inputBtn.tooltip = atom.tooltips.add @inputBtn, 53 | title: 'Insert Text' 54 | 55 | @prevHeight = atom.config.get('platformio-ide-terminal.style.defaultPanelHeight') 56 | if @prevHeight.indexOf('%') > 0 57 | percent = Math.abs(Math.min(parseFloat(@prevHeight) / 100.0, 1)) 58 | bottomHeight = $('atom-panel.bottom').children(".terminal-view").height() or 0 59 | @prevHeight = percent * ($('.item-views').height() + bottomHeight) 60 | @xterm.height 0 61 | 62 | @setAnimationSpeed() 63 | @subscriptions.add atom.config.onDidChange 'platformio-ide-terminal.style.animationSpeed', @setAnimationSpeed 64 | 65 | @updateToolbarVisibility() 66 | @subscriptions.add atom.config.onDidChange 'platformio-ide-terminal.toggles.showToolbar', @updateToolbarVisibility 67 | 68 | override = (event) -> 69 | return if event.originalEvent.dataTransfer.getData('platformio-ide-terminal') is 'true' 70 | event.preventDefault() 71 | event.stopPropagation() 72 | 73 | @xterm.on 'mouseup', (event) => 74 | if event.which != 3 75 | text = window.getSelection().toString() 76 | if atom.config.get('platformio-ide-terminal.toggles.selectToCopy') and text 77 | rawLines = text.split(/\r?\n/g) 78 | lines = rawLines.map (line) -> 79 | line.replace(/\s/g, " ").trimRight() 80 | text = lines.join("\n") 81 | atom.clipboard.write(text) 82 | unless text 83 | @focus() 84 | @xterm.on 'dragenter', override 85 | @xterm.on 'dragover', override 86 | @xterm.on 'drop', @recieveItemOrFile 87 | 88 | @on 'focus', @focus 89 | @subscriptions.add dispose: => 90 | @off 'focus', @focus 91 | 92 | if /zsh|bash/.test(@shell) and @args.indexOf('--login') == -1 and Pty.platform isnt 'win32' and atom.config.get('platformio-ide-terminal.toggles.loginShell') 93 | @args.unshift '--login' 94 | 95 | attach: -> 96 | return if @panel? 97 | @panel = atom.workspace.addBottomPanel(item: this, visible: false) 98 | 99 | setAnimationSpeed: => 100 | @animationSpeed = atom.config.get('platformio-ide-terminal.style.animationSpeed') 101 | @animationSpeed = 100 if @animationSpeed is 0 102 | 103 | @xterm.css 'transition', "height #{0.25 / @animationSpeed}s linear" 104 | 105 | updateToolbarVisibility: => 106 | @showToolbar = atom.config.get('platformio-ide-terminal.toggles.showToolbar') 107 | if @showToolbar 108 | @toolbar.css 'display', 'block' 109 | else 110 | @toolbar.css 'display', 'none' 111 | 112 | recieveItemOrFile: (event) => 113 | event.preventDefault() 114 | event.stopPropagation() 115 | {dataTransfer} = event.originalEvent 116 | 117 | if dataTransfer.getData('atom-event') is 'true' 118 | filePath = dataTransfer.getData('text/plain') 119 | @input "#{filePath} " if filePath 120 | else if filePath = dataTransfer.getData('initialPath') 121 | @input "#{filePath} " 122 | else if dataTransfer.files.length > 0 123 | for file in dataTransfer.files 124 | @input "#{file.path} " 125 | 126 | forkPtyProcess: -> 127 | Task.once Pty, path.resolve(@pwd), @shell, @args, @env, => 128 | @input = -> 129 | @resize = -> 130 | 131 | getId: -> 132 | return @id 133 | 134 | displayTerminal: -> 135 | {cols, rows} = @getDimensions() 136 | @ptyProcess = @forkPtyProcess() 137 | 138 | @terminal = new Terminal { 139 | cursorBlink : false 140 | scrollback : atom.config.get 'platformio-ide-terminal.core.scrollback' 141 | cols, rows 142 | } 143 | 144 | @attachListeners() 145 | @attachResizeEvents() 146 | @attachWindowEvents() 147 | @terminal.open @xterm.get(0) 148 | 149 | attachListeners: -> 150 | @ptyProcess.on "platformio-ide-terminal:data", (data) => 151 | @terminal.write data 152 | 153 | @ptyProcess.on "platformio-ide-terminal:exit", => 154 | @destroy() if atom.config.get('platformio-ide-terminal.toggles.autoClose') 155 | 156 | @terminal.end = => @destroy() 157 | 158 | @terminal.on "data", (data) => 159 | @input data 160 | 161 | @ptyProcess.on "platformio-ide-terminal:title", (title) => 162 | @process = title 163 | @terminal.on "title", (title) => 164 | @title = title 165 | 166 | @terminal.once "open", => 167 | @applyStyle() 168 | @resizeTerminalToView() 169 | 170 | return unless @ptyProcess.childProcess? 171 | autoRunCommand = atom.config.get('platformio-ide-terminal.core.autoRunCommand') 172 | @input "#{autoRunCommand}#{os.EOL}" if autoRunCommand 173 | @input "#{command}#{os.EOL}" for command in @autoRun 174 | 175 | destroy: -> 176 | @subscriptions.dispose() 177 | @statusIcon.destroy() 178 | @statusBar.removeTerminalView this 179 | @detachResizeEvents() 180 | @detachWindowEvents() 181 | 182 | if @panel.isVisible() 183 | @hide() 184 | @onTransitionEnd => @panel.destroy() 185 | else 186 | @panel.destroy() 187 | 188 | if @statusIcon and @statusIcon.parentNode 189 | @statusIcon.parentNode.removeChild(@statusIcon) 190 | 191 | @ptyProcess?.terminate() 192 | @terminal?.destroy() 193 | 194 | maximize: -> 195 | @subscriptions.remove @maximizeBtn.tooltip 196 | @maximizeBtn.tooltip.dispose() 197 | 198 | @maxHeight = @prevHeight + atom.workspace.getCenter().paneContainer.element.offsetHeight 199 | btn = @maximizeBtn.children('span') 200 | @onTransitionEnd => @focus() 201 | 202 | if @maximized 203 | @maximizeBtn.tooltip = atom.tooltips.add @maximizeBtn, 204 | title: 'Fullscreen' 205 | @subscriptions.add @maximizeBtn.tooltip 206 | @adjustHeight @prevHeight 207 | btn.removeClass('icon-screen-normal').addClass('icon-screen-full') 208 | @maximized = false 209 | else 210 | @maximizeBtn.tooltip = atom.tooltips.add @maximizeBtn, 211 | title: 'Normal' 212 | @subscriptions.add @maximizeBtn.tooltip 213 | @adjustHeight @maxHeight 214 | btn.removeClass('icon-screen-full').addClass('icon-screen-normal') 215 | @maximized = true 216 | 217 | open: => 218 | lastActiveElement ?= $(document.activeElement) 219 | 220 | if lastOpenedView and lastOpenedView != this 221 | if lastOpenedView.maximized 222 | @subscriptions.remove @maximizeBtn.tooltip 223 | @maximizeBtn.tooltip.dispose() 224 | icon = @maximizeBtn.children('span') 225 | 226 | @maxHeight = lastOpenedView.maxHeight 227 | @maximizeBtn.tooltip = atom.tooltips.add @maximizeBtn, 228 | title: 'Normal' 229 | @subscriptions.add @maximizeBtn.tooltip 230 | icon.removeClass('icon-screen-full').addClass('icon-screen-normal') 231 | @maximized = true 232 | lastOpenedView.hide() 233 | 234 | lastOpenedView = this 235 | @statusBar.setActiveTerminalView this 236 | @statusIcon.activate() 237 | 238 | @onTransitionEnd => 239 | if not @opened 240 | @opened = true 241 | @displayTerminal() 242 | @prevHeight = @nearestRow(@xterm.height()) 243 | @xterm.height(@prevHeight) 244 | @emit "platformio-ide-terminal:terminal-open" 245 | else 246 | @focus() 247 | 248 | @panel.show() 249 | @xterm.height 0 250 | @animating = true 251 | @xterm.height if @maximized then @maxHeight else @prevHeight 252 | 253 | hide: => 254 | @terminal?.blur() 255 | lastOpenedView = null 256 | @statusIcon.deactivate() 257 | 258 | @onTransitionEnd => 259 | @panel.hide() 260 | unless lastOpenedView? 261 | if lastActiveElement? 262 | lastActiveElement.focus() 263 | lastActiveElement = null 264 | 265 | @xterm.height if @maximized then @maxHeight else @prevHeight 266 | @animating = true 267 | @xterm.height 0 268 | 269 | toggle: -> 270 | return if @animating 271 | 272 | if @panel.isVisible() 273 | @hide() 274 | else 275 | @open() 276 | 277 | input: (data) -> 278 | return unless @ptyProcess.childProcess? 279 | 280 | @terminal.stopScrolling() 281 | @ptyProcess.send event: 'input', text: data 282 | 283 | resize: (cols, rows) -> 284 | return unless @ptyProcess.childProcess? 285 | 286 | @ptyProcess.send {event: 'resize', rows, cols} 287 | 288 | pty: () -> 289 | if not @opened 290 | wait = new Promise (resolve, reject) => 291 | @emitter.on "platformio-ide-terminal:terminal-open", () => 292 | resolve() 293 | setTimeout reject, 1000 294 | 295 | wait.then () => 296 | @ptyPromise() 297 | else 298 | @ptyPromise() 299 | 300 | ptyPromise: () -> 301 | new Promise (resolve, reject) => 302 | if @ptyProcess? 303 | @ptyProcess.on "platformio-ide-terminal:pty", (pty) => 304 | resolve(pty) 305 | @ptyProcess.send {event: 'pty'} 306 | setTimeout reject, 1000 307 | else 308 | reject() 309 | 310 | applyStyle: -> 311 | config = atom.config.get 'platformio-ide-terminal' 312 | 313 | @xterm.addClass config.style.theme 314 | 315 | @subscriptions.add atom.config.onDidChange 'platformio-ide-terminal.style.theme', (event) => 316 | @xterm.removeClass event.oldValue 317 | @xterm.addClass event.newValue 318 | 319 | @xterm.addClass 'cursor-blink' if config.toggles.cursorBlink 320 | 321 | editorFont = atom.config.get('editor.fontFamily') 322 | defaultFont = "Menlo, Consolas, 'DejaVu Sans Mono', monospace" 323 | overrideFont = config.style.fontFamily 324 | @terminal.element.style.fontFamily = overrideFont or editorFont or defaultFont 325 | 326 | @subscriptions.add atom.config.onDidChange 'editor.fontFamily', (event) => 327 | editorFont = event.newValue 328 | @terminal.element.style.fontFamily = overrideFont or editorFont or defaultFont 329 | @subscriptions.add atom.config.onDidChange 'platformio-ide-terminal.style.fontFamily', (event) => 330 | overrideFont = event.newValue 331 | @terminal.element.style.fontFamily = overrideFont or editorFont or defaultFont 332 | 333 | editorFontSize = atom.config.get('editor.fontSize') 334 | overrideFontSize = config.style.fontSize 335 | @terminal.element.style.fontSize = "#{overrideFontSize or editorFontSize}px" 336 | 337 | @subscriptions.add atom.config.onDidChange 'editor.fontSize', (event) => 338 | editorFontSize = event.newValue 339 | @terminal.element.style.fontSize = "#{overrideFontSize or editorFontSize}px" 340 | @resizeTerminalToView() 341 | @subscriptions.add atom.config.onDidChange 'platformio-ide-terminal.style.fontSize', (event) => 342 | overrideFontSize = event.newValue 343 | @terminal.element.style.fontSize = "#{overrideFontSize or editorFontSize}px" 344 | @resizeTerminalToView() 345 | 346 | # first 8 colors i.e. 'dark' colors 347 | @terminal.colors[0..7] = [ 348 | config.ansiColors.normal.black.toHexString() 349 | config.ansiColors.normal.red.toHexString() 350 | config.ansiColors.normal.green.toHexString() 351 | config.ansiColors.normal.yellow.toHexString() 352 | config.ansiColors.normal.blue.toHexString() 353 | config.ansiColors.normal.magenta.toHexString() 354 | config.ansiColors.normal.cyan.toHexString() 355 | config.ansiColors.normal.white.toHexString() 356 | ] 357 | # 'bright' colors 358 | @terminal.colors[8..15] = [ 359 | config.ansiColors.zBright.brightBlack.toHexString() 360 | config.ansiColors.zBright.brightRed.toHexString() 361 | config.ansiColors.zBright.brightGreen.toHexString() 362 | config.ansiColors.zBright.brightYellow.toHexString() 363 | config.ansiColors.zBright.brightBlue.toHexString() 364 | config.ansiColors.zBright.brightMagenta.toHexString() 365 | config.ansiColors.zBright.brightCyan.toHexString() 366 | config.ansiColors.zBright.brightWhite.toHexString() 367 | ] 368 | 369 | attachWindowEvents: -> 370 | $(window).on 'resize', @onWindowResize 371 | 372 | detachWindowEvents: -> 373 | $(window).off 'resize', @onWindowResize 374 | 375 | attachResizeEvents: -> 376 | @panelDivider.on 'mousedown', @resizeStarted 377 | 378 | detachResizeEvents: -> 379 | @panelDivider.off 'mousedown' 380 | 381 | onWindowResize: => 382 | if not @tabView 383 | @xterm.css 'transition', '' 384 | newHeight = $(window).height() 385 | bottomPanel = $('atom-panel-container.bottom').first().get(0) 386 | overflow = bottomPanel.scrollHeight - bottomPanel.offsetHeight 387 | 388 | delta = newHeight - @windowHeight 389 | @windowHeight = newHeight 390 | 391 | if @maximized 392 | clamped = Math.max(@maxHeight + delta, @rowHeight) 393 | 394 | @adjustHeight clamped if @panel.isVisible() 395 | @maxHeight = clamped 396 | 397 | @prevHeight = Math.min(@prevHeight, @maxHeight) 398 | else if overflow > 0 399 | clamped = Math.max(@nearestRow(@prevHeight + delta), @rowHeight) 400 | 401 | @adjustHeight clamped if @panel.isVisible() 402 | @prevHeight = clamped 403 | 404 | @xterm.css 'transition', "height #{0.25 / @animationSpeed}s linear" 405 | @resizeTerminalToView() 406 | 407 | resizeStarted: => 408 | return if @maximized 409 | @maxHeight = @prevHeight + $('.item-views').height() 410 | $(document).on('mousemove', @resizePanel) 411 | $(document).on('mouseup', @resizeStopped) 412 | @xterm.css 'transition', '' 413 | 414 | resizeStopped: => 415 | $(document).off('mousemove', @resizePanel) 416 | $(document).off('mouseup', @resizeStopped) 417 | @xterm.css 'transition', "height #{0.25 / @animationSpeed}s linear" 418 | 419 | nearestRow: (value) -> 420 | rows = value // @rowHeight 421 | return rows * @rowHeight 422 | 423 | resizePanel: (event) => 424 | return @resizeStopped() unless event.which is 1 425 | 426 | mouseY = $(window).height() - event.pageY 427 | delta = mouseY - $('atom-panel-container.bottom').height() - $('atom-panel-container.footer').height() 428 | return unless Math.abs(delta) > (@rowHeight * 5 / 6) 429 | 430 | clamped = Math.max(@nearestRow(@prevHeight + delta), @rowHeight) 431 | return if clamped > @maxHeight 432 | 433 | @xterm.height clamped 434 | $(@terminal.element).height clamped 435 | @prevHeight = clamped 436 | 437 | @resizeTerminalToView() 438 | 439 | adjustHeight: (height) -> 440 | @xterm.height height 441 | $(@terminal.element).height height 442 | 443 | copy: -> 444 | if @terminal._selected 445 | textarea = @terminal.getCopyTextarea() 446 | text = @terminal.grabText( 447 | @terminal._selected.x1, @terminal._selected.x2, 448 | @terminal._selected.y1, @terminal._selected.y2) 449 | else 450 | rawText = @terminal.context.getSelection().toString() 451 | rawLines = rawText.split(/\r?\n/g) 452 | lines = rawLines.map (line) -> 453 | line.replace(/\s/g, " ").trimRight() 454 | text = lines.join("\n") 455 | atom.clipboard.write text 456 | 457 | paste: -> 458 | @input atom.clipboard.read() 459 | 460 | insertSelection: (customText) -> 461 | return unless editor = atom.workspace.getActiveTextEditor() 462 | runCommand = atom.config.get('platformio-ide-terminal.toggles.runInsertedText') 463 | selectionText = '' 464 | if selection = editor.getSelectedText() 465 | @terminal.stopScrolling() 466 | selectionText = selection 467 | else if cursor = editor.getCursorBufferPosition() 468 | line = editor.lineTextForBufferRow(cursor.row) 469 | @terminal.stopScrolling() 470 | selectionText = line 471 | editor.moveDown(1); 472 | @input "#{customText. 473 | replace(/\$L/, "#{editor.getCursorBufferPosition().row + 1}"). 474 | replace(/\$F/, path.basename(if editor.buffer.getPath() then editor.buffer.getPath() else '.')). 475 | replace(/\$D/, path.dirname(if editor.buffer.getPath() then editor.buffer.getPath() else '.')). 476 | replace(/\$S/, selectionText). 477 | replace(/\$\$/, '$')}#{if runCommand then os.EOL else ''}" 478 | 479 | focus: (fromWindowEvent) => 480 | @resizeTerminalToView() 481 | @focusTerminal(fromWindowEvent) 482 | @statusBar.setActiveTerminalView(this) 483 | super() 484 | 485 | blur: => 486 | @blurTerminal() 487 | super() 488 | 489 | focusTerminal: (fromWindowEvent) => 490 | return unless @terminal 491 | 492 | lastActiveElement = $(document.activeElement) 493 | return if fromWindowEvent and not (lastActiveElement.is('div.terminal') or lastActiveElement.parents('div.terminal').length) 494 | 495 | @terminal.focus() 496 | if @terminal._textarea 497 | @terminal._textarea.focus() 498 | else 499 | @terminal.element.focus() 500 | 501 | blurTerminal: => 502 | return unless @terminal 503 | 504 | @terminal.blur() 505 | @terminal.element.blur() 506 | 507 | if lastActiveElement? 508 | lastActiveElement.focus() 509 | 510 | resizeTerminalToView: -> 511 | return unless @panel.isVisible() or @tabView 512 | 513 | {cols, rows} = @getDimensions() 514 | return unless cols > 0 and rows > 0 515 | return unless @terminal 516 | return if @terminal.rows is rows and @terminal.cols is cols 517 | 518 | @resize cols, rows 519 | @terminal.resize cols, rows 520 | 521 | getDimensions: -> 522 | fakeRow = $("
     
    ") 523 | 524 | if @terminal 525 | @find('.terminal').append fakeRow 526 | fakeCol = fakeRow.children().first()[0].getBoundingClientRect() 527 | cols = Math.floor @xterm.width() / (fakeCol.width or 9) 528 | rows = Math.floor @xterm.height() / (fakeCol.height or 20) 529 | @rowHeight = fakeCol.height 530 | fakeRow.remove() 531 | else 532 | cols = Math.floor @xterm.width() / 9 533 | rows = Math.floor @xterm.height() / 20 534 | 535 | {cols, rows} 536 | 537 | onTransitionEnd: (callback) -> 538 | @xterm.one 'webkitTransitionEnd', => 539 | callback() 540 | @animating = false 541 | 542 | inputDialog: -> 543 | InputDialog ?= require('./input-dialog') 544 | dialog = new InputDialog this 545 | dialog.attach() 546 | 547 | rename: -> 548 | @statusIcon.rename() 549 | 550 | toggleTabView: -> 551 | if @tabView 552 | @panel = atom.workspace.addBottomPanel(item: this, visible: false) 553 | @attachResizeEvents() 554 | @closeBtn.show() 555 | @hideBtn.show() 556 | @maximizeBtn.show() 557 | @tabView = false 558 | else 559 | @panel.destroy() 560 | @detachResizeEvents() 561 | @closeBtn.hide() 562 | @hideBtn.hide() 563 | @maximizeBtn.hide() 564 | @xterm.css "height", "" 565 | @tabView = true 566 | lastOpenedView = null if lastOpenedView == this 567 | 568 | getTitle: -> 569 | @statusIcon.getName() or "platformio-ide-terminal" 570 | 571 | getIconName: -> 572 | "terminal" 573 | 574 | getShell: -> 575 | return path.basename @shell 576 | 577 | getShellPath: -> 578 | return @shell 579 | 580 | emit: (event, data) -> 581 | @emitter.emit event, data 582 | 583 | onDidChangeTitle: (callback) -> 584 | @emitter.on 'did-change-title', callback 585 | 586 | getPath: -> 587 | return @getTerminalTitle() 588 | 589 | getTerminalTitle: -> 590 | return @title or @process 591 | 592 | getTerminal: -> 593 | return @terminal 594 | 595 | isAnimating: -> 596 | return @animating 597 | -------------------------------------------------------------------------------- /menus/platformio-ide-terminal.cson: -------------------------------------------------------------------------------- 1 | 'context-menu': 2 | '.platformio-ide-terminal .pio-terminal-status-icon': [ 3 | {label: 'Red', command: 'platformio-ide-terminal:status-red'}, 4 | {label: 'Orange', command: 'platformio-ide-terminal:status-orange'}, 5 | {label: 'Yellow', command: 'platformio-ide-terminal:status-yellow'}, 6 | {label: 'Green', command: 'platformio-ide-terminal:status-green'}, 7 | {label: 'Blue', command: 'platformio-ide-terminal:status-blue'}, 8 | {label: 'Purple', command: 'platformio-ide-terminal:status-purple'}, 9 | {label: 'Pink', command: 'platformio-ide-terminal:status-pink'}, 10 | {label: 'Cyan', command: 'platformio-ide-terminal:status-cyan'}, 11 | {label: 'Magenta', command: 'platformio-ide-terminal:status-magenta'}, 12 | {label: 'Default', command: 'platformio-ide-terminal:status-default'}, 13 | {type: 'separator'}, 14 | {label: 'Hide', command: 'platformio-ide-terminal:context-hide'}, 15 | {label: 'Rename', command: 'platformio-ide-terminal:context-rename'}, 16 | {label: 'Close', command: 'platformio-ide-terminal:context-close'} 17 | ] 18 | '.platformio-ide-terminal .terminal': [ 19 | {label: 'Copy', command: 'platformio-ide-terminal:copy'}, 20 | {label: 'Paste', command: 'platformio-ide-terminal:paste'}, 21 | {type: 'separator'}, 22 | {label: 'Hide', command: 'platformio-ide-terminal:hide'}, 23 | {label: 'Close', command: 'platformio-ide-terminal:close'} 24 | {type: 'separator'} 25 | {label: "Clear", command: 'platformio-ide-terminal:clear'} 26 | ] 27 | 'menu': [ 28 | { 29 | 'label': 'Packages' 30 | 'submenu': [ 31 | 'label': 'platformio-ide-terminal' 32 | 'submenu': [ 33 | { 34 | 'label': 'Toggle' 35 | 'command': 'platformio-ide-terminal:toggle' 36 | }, 37 | { 38 | 'label': 'Focus' 39 | 'command': 'platformio-ide-terminal:focus' 40 | }, 41 | { 42 | 'label': 'New Terminal' 43 | 'command': 'platformio-ide-terminal:new' 44 | }, 45 | { 46 | 'label': 'Previous Terminal' 47 | 'command': 'platformio-ide-terminal:prev' 48 | }, 49 | { 50 | 'label': 'Next Terminal' 51 | 'command': 'platformio-ide-terminal:next' 52 | }, 53 | { 54 | 'label': 'Toggle Fullscreen' 55 | 'command': 'platformio-ide-terminal:fullscreen' 56 | }, 57 | { 58 | 'label': 'Close Terminal' 59 | 'command': 'platformio-ide-terminal:close' 60 | }, 61 | { 62 | 'label': 'Close All Terminals' 63 | 'command': 'platformio-ide-terminal:close-all' 64 | } 65 | ] 66 | ] 67 | } 68 | ] 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platformio-ide-terminal", 3 | "main": "./lib/platformio-ide-terminal", 4 | "version": "2.10.1", 5 | "description": "A terminal package for Atom, complete with themes, API and more for PlatformIO IDE. Fork of terminal-plus.", 6 | "author": "Jeremy Ebneyamin", 7 | "keywords": [ 8 | "PlatformIO", 9 | "terminal-plus", 10 | "terminal", 11 | "iterm", 12 | "panel", 13 | "pty", 14 | "tty", 15 | "xterm", 16 | "iterm", 17 | "iterm2", 18 | "console", 19 | "powershell" 20 | ], 21 | "contributors": [ 22 | { 23 | "name": "Ivan Kravets", 24 | "email": "me@ikravets.com", 25 | "url": "http://platformio.org" 26 | }, 27 | { 28 | "name": "Dmytro Kyrychuk", 29 | "email": "dmytro.kyrychuck@gmail.com", 30 | "url": "http://platformio.org" 31 | }, 32 | { 33 | "name": "The Community Contributors", 34 | "url": "https://github.com/platformio/platformio-atom-ide-terminal/graphs/contributors" 35 | } 36 | ], 37 | "homepage": "https://atom.io/packages/platformio-ide-terminal", 38 | "repository": "https://github.com/platformio/platformio-atom-ide-terminal", 39 | "license": "MIT", 40 | "engines": { 41 | "atom": ">=1.12.2 <2.0.0" 42 | }, 43 | "dependencies": { 44 | "atom-space-pen-views": "^2.2.0", 45 | "node-pty-prebuilt-multiarch": "^0.9.0", 46 | "term.js": "https://github.com/jeremyramin/term.js/tarball/master", 47 | "underscore": "^1.8.3" 48 | }, 49 | "activationHooks": [ 50 | "core:loaded-shell-environment" 51 | ], 52 | "consumedServices": { 53 | "status-bar": { 54 | "versions": { 55 | "^1.0.0": "consumeStatusBar" 56 | } 57 | } 58 | }, 59 | "providedServices": { 60 | "platformioIDETerminal": { 61 | "description": "PlatformIO IDE Terminal API", 62 | "versions": { 63 | "1.1.0": "providePlatformIOIDETerminal" 64 | } 65 | }, 66 | "runInTerminal": { 67 | "description": "Deprecated API for PlatformIO IDE 1.0", 68 | "versions": { 69 | "0.14.5": "provideRunInTerminal" 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /resources/demo copy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/demo copy.gif -------------------------------------------------------------------------------- /resources/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/demo.gif -------------------------------------------------------------------------------- /resources/full_screen_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/full_screen_demo.png -------------------------------------------------------------------------------- /resources/insert_selected_text.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/insert_selected_text.gif -------------------------------------------------------------------------------- /resources/insert_text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/insert_text.png -------------------------------------------------------------------------------- /resources/map_terminals_to_auto_open.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/map_terminals_to_auto_open.gif -------------------------------------------------------------------------------- /resources/map_terminals_to_file.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/map_terminals_to_file.gif -------------------------------------------------------------------------------- /resources/map_terminals_to_folder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/map_terminals_to_folder.gif -------------------------------------------------------------------------------- /resources/plus-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/plus-icon.png -------------------------------------------------------------------------------- /resources/red-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/red-x.png -------------------------------------------------------------------------------- /resources/sorting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/sorting.gif -------------------------------------------------------------------------------- /resources/special_keys.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/special_keys.gif -------------------------------------------------------------------------------- /resources/status-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/status-bar.png -------------------------------------------------------------------------------- /resources/status-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/status-icon.png -------------------------------------------------------------------------------- /resources/status-icon_color_coding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/status-icon_color_coding.png -------------------------------------------------------------------------------- /resources/status-icon_rename-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/status-icon_rename-dialog.png -------------------------------------------------------------------------------- /resources/status-icon_rename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/status-icon_rename.png -------------------------------------------------------------------------------- /resources/terminal_title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/terminal_title.png -------------------------------------------------------------------------------- /resources/terminal_with_status-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platformio/platformio-atom-ide-terminal/d95b216d26f4062b058a5a0d4b966f5bac177383/resources/terminal_with_status-bar.png -------------------------------------------------------------------------------- /spec/platformio-ide-terminal-spec.coffee: -------------------------------------------------------------------------------- 1 | PlatformIOTerminal = require '../lib/platformio-ide-terminal' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "PlatformIOTerminal", -> 9 | [workspaceElement, activationPromise] = [] 10 | 11 | beforeEach -> 12 | workspaceElement = atom.views.getView(atom.workspace) 13 | activationPromise = atom.packages.activatePackage('platformio-ide-terminal') 14 | 15 | describe "when the platformio-ide-terminal:toggle event is triggered", -> 16 | it "hides and shows the modal panel", -> 17 | # Before the activation event the view is not on the DOM, and no panel 18 | # has been created 19 | expect(workspaceElement.querySelector('.platformio-ide-terminal')).not.toExist() 20 | 21 | # This is an activation event, triggering it will cause the package to be 22 | # activated. 23 | atom.commands.dispatch workspaceElement, 'platformio-ide-terminal:toggle' 24 | 25 | waitsForPromise -> 26 | activationPromise 27 | 28 | runs -> 29 | expect(workspaceElement.querySelector('.platformio-ide-terminal')).toExist() 30 | 31 | platformIOTerminalElement = workspaceElement.querySelector('.platformio-ide-terminal') 32 | expect(platformIOTerminalElement).toExist() 33 | 34 | statusBar = atom.workspace.panelForItem(platformIOTerminalElement) 35 | expect(statusBar.isVisible()).toBe true 36 | atom.commands.dispatch workspaceElement, 'platformio-ide-terminal:toggle' 37 | expect(statusBar.isVisible()).toBe false 38 | 39 | it "hides and shows the view", -> 40 | # This test shows you an integration test testing at the view level. 41 | 42 | # Attaching the workspaceElement to the DOM is required to allow the 43 | # `toBeVisible()` matchers to work. Anything testing visibility or focus 44 | # requires that the workspaceElement is on the DOM. Tests that attach the 45 | # workspaceElement to the DOM are generally slower than those off DOM. 46 | jasmine.attachToDOM(workspaceElement) 47 | 48 | expect(workspaceElement.querySelector('.platformio-ide-terminal')).not.toExist() 49 | 50 | # This is an activation event, triggering it causes the package to be 51 | # activated. 52 | atom.commands.dispatch workspaceElement, 'platformio-ide-terminal:toggle' 53 | 54 | waitsForPromise -> 55 | activationPromise 56 | 57 | runs -> 58 | # Now we can test for view visibility 59 | platformIOTerminalElement = workspaceElement.querySelector('.platformio-ide-terminal') 60 | expect(platformIOTerminalElement).toBeVisible() 61 | atom.commands.dispatch workspaceElement, 'platformio-ide-terminal:toggle' 62 | expect(platformIOTerminalElement).not.toBeVisible() 63 | -------------------------------------------------------------------------------- /spec/platformio-ide-terminal-view-spec.coffee: -------------------------------------------------------------------------------- 1 | PlatformIOTerminalView = require '../lib/platformio-ide-terminal-view' 2 | 3 | describe "PlatformIOTerminalView", -> 4 | it "has one valid test", -> 5 | expect("life").toBe "easy" 6 | -------------------------------------------------------------------------------- /styles/platformio-ide-terminal.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | @icon-padding: 5px; 4 | @icon-width: 16px; 5 | @icon-height: 16px; 6 | @status-bar-height: 20px; 7 | @inactive-opacity: 0.5; 8 | 9 | .platformio-ide-terminal { 10 | letter-spacing: normal; 11 | position: inherit; 12 | 13 | ul { 14 | padding: 0; 15 | margin: 0; 16 | } 17 | .icon::before { 18 | margin: 0; 19 | padding: 0; 20 | 21 | } 22 | .icon-x::before { 23 | content: '\f081' !important; 24 | line-height: normal; 25 | } 26 | 27 | &.terminal-view { 28 | 29 | .panel-divider { 30 | order: 1; 31 | display: inherit; 32 | 33 | flex: 0 0 auto; 34 | position: absolute; 35 | height: 8px; 36 | margin-top: -4px; 37 | 38 | z-index: 3; 39 | -webkit-user-select: none; 40 | cursor: ns-resize; 41 | width: 100%; 42 | } 43 | 44 | .input-block{ 45 | padding: 0.375em; 46 | 47 | .btn-toolbar { 48 | padding: 0.375em; 49 | 50 | .left { 51 | float: left; 52 | } 53 | .right { 54 | float: right; 55 | } 56 | } 57 | } 58 | 59 | .xterm { 60 | display: -webkit-flex; 61 | order: 3; 62 | flex: 1; 63 | width: 100%; 64 | height: 0px; 65 | padding: 0 5px; 66 | text-shadow: none; 67 | 68 | > .terminal { 69 | display: -webkit-flex; 70 | 71 | flex-direction: column; 72 | align-content: flex-end; 73 | position: relative; 74 | 75 | width: 100%; 76 | overflow: hidden; 77 | > div { 78 | flex: 1 0 auto; 79 | white-space: nowrap; 80 | font-size: inherit; 81 | user-select: text; 82 | -ms-user-select: text; 83 | -webkit-user-select: text; 84 | > span { 85 | display: inline-block; 86 | line-height: inherit; 87 | height: inherit; 88 | } 89 | } 90 | } 91 | } 92 | 93 | .cursor-blink { 94 | .terminal-cursor { 95 | -webkit-animation: blink 1s step-start 0s infinite; 96 | -webkit-animation-delay: 1s; 97 | @-webkit-keyframes blink { 98 | 50% { background: transparent; } 99 | } 100 | } 101 | } 102 | } 103 | 104 | &.status-bar { 105 | position: relative; 106 | vertical-align: middle; 107 | display: inline; 108 | -webkit-user-select: none; 109 | margin: 0 10px; 110 | box-sizing: content-box; 111 | height: @status-bar-height; 112 | 113 | i { 114 | cursor: pointer; 115 | display: inherit; 116 | text-align: center; 117 | align-self: center; 118 | line-height: @status-bar-height; 119 | } 120 | .icon-x { 121 | color: #9da5b4; 122 | justify-content: flex-end; 123 | } 124 | 125 | .status-container { 126 | display: inherit; 127 | position: relative; 128 | width: 100%; 129 | height: 100%; 130 | line-height: @status-bar-height; 131 | margin: 0 @icon-padding; 132 | 133 | .pio-terminal-status-icon { 134 | display: inherit; 135 | cursor: pointer; 136 | -webkit-transition: opacity 0.25s ease-in-out, color 0.5s ease-in-out; 137 | -webkit-user-drag: element; 138 | padding: 0 @icon-padding; 139 | opacity: @inactive-opacity; 140 | height: 100%; 141 | &.active { 142 | opacity: 1; 143 | } 144 | } 145 | 146 | .is-dragging { 147 | border-bottom: 1px solid @background-color-info; 148 | } 149 | 150 | .placeholder { 151 | pointer-events: none; 152 | position: absolute; 153 | top: -@icon-height; 154 | color: @background-color-info; 155 | z-index: 999; 156 | margin: 0 -6px; 157 | padding: 0; 158 | height: 10px; 159 | line-height: 10px; 160 | 161 | &::before { 162 | font-size: 16px; 163 | font-family: 'Octicons Regular'; 164 | font-weight: normal; 165 | font-style: normal; 166 | display: inline-block; 167 | -webkit-font-smoothing: antialiased; 168 | text-decoration: none; 169 | content: "\f05b"; 170 | } 171 | } 172 | 173 | // &:after { 174 | // font-family: 'Octicons Regular'; 175 | // font-weight: normal; 176 | // font-style: normal; 177 | // -webkit-font-smoothing: antialiased; 178 | // text-decoration: none; 179 | // font-size: 16px; 180 | // line-height: 8px; 181 | // content: "\f05b"; 182 | // } 183 | } 184 | } 185 | 186 | .inserted { 187 | -webkit-animation: flash 0.25s linear 6 alternate; 188 | } 189 | @-webkit-keyframes flash { 190 | 0% { 191 | opacity: @inactive-opacity; 192 | } 193 | 100% { 194 | opacity: 1; 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /styles/themes.less: -------------------------------------------------------------------------------- 1 | .terminal-view { 2 | @import 'themes/standard'; 3 | @import 'themes/linux'; 4 | @import 'themes/homebrew'; 5 | @import 'themes/grass'; 6 | @import 'themes/man-page'; 7 | @import 'themes/inverse'; 8 | @import 'themes/novel'; 9 | @import 'themes/ocean'; 10 | @import 'themes/pro'; 11 | @import 'themes/red'; 12 | @import 'themes/red-sands'; 13 | @import 'themes/silver-aerogel'; 14 | @import 'themes/solarized-dark'; 15 | @import 'themes/solarized-light'; 16 | @import 'themes/solid-colors'; 17 | @import 'themes/dracula'; 18 | @import 'themes/one-dark'; 19 | @import 'themes/christmas'; 20 | @import 'themes/predawn'; 21 | @import 'themes/city-lights'; 22 | } 23 | -------------------------------------------------------------------------------- /styles/themes/christmas.less: -------------------------------------------------------------------------------- 1 | .christmas { 2 | background-color: #0C0047; 3 | color: #F81705; 4 | 5 | ::selection { 6 | background-color: #298F16; 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #009F59; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/city-lights.less: -------------------------------------------------------------------------------- 1 | .city-lights { 2 | background-color: #181d23; 3 | color: #666d81; 4 | 5 | ::selection { 6 | background-color: #2a2f38; 7 | color: #b7c5d3; 8 | } 9 | 10 | .terminal-cursor { 11 | background-color: #528BFF; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /styles/themes/dracula.less: -------------------------------------------------------------------------------- 1 | .dracula { 2 | background-color: #1e1f29; 3 | color: white; 4 | 5 | ::selection { 6 | background-color: #44475a; 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #999999; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/grass.less: -------------------------------------------------------------------------------- 1 | .grass { 2 | background-color: rgb(19, 119, 61); 3 | color: rgb(255, 240, 165); 4 | 5 | ::selection { 6 | background-color: rgba(182, 73, 38, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(142, 40, 0); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/homebrew.less: -------------------------------------------------------------------------------- 1 | .homebrew { 2 | background-color: black; 3 | color: rgb(41, 254, 20); 4 | 5 | ::selection { 6 | background-color: rgba(7, 30, 155, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(55, 254, 38); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/inverse.less: -------------------------------------------------------------------------------- 1 | .inverse { 2 | background-color: white; 3 | color: black; 4 | 5 | ::selection { 6 | background-color: rgba(178, 215, 255, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(146, 146, 146); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/linux.less: -------------------------------------------------------------------------------- 1 | .linux { 2 | background-color: black; 3 | color: rgb(230,230,230); 4 | 5 | ::selection { 6 | background-color: rgba(155, 30, 7, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(200,20,25); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/man-page.less: -------------------------------------------------------------------------------- 1 | .man-page { 2 | background-color: rgb(254, 244, 156); 3 | color: black; 4 | 5 | ::selection { 6 | background-color: rgba(178, 215, 255, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(146, 146, 146); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/novel.less: -------------------------------------------------------------------------------- 1 | .novel { 2 | background-color: rgb(223, 219, 196); 3 | color: rgb(77, 47, 46); 4 | 5 | ::selection { 6 | background-color: rgba(155, 153, 122, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(115, 99, 89); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/ocean.less: -------------------------------------------------------------------------------- 1 | .ocean { 2 | background-color: rgb(44, 102, 201); 3 | color: white; 4 | 5 | ::selection { 6 | background-color: rgba(41, 134, 255, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(146, 146, 146); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/one-dark.less: -------------------------------------------------------------------------------- 1 | .one-dark { 2 | background-color: #282C34; 3 | color: #ABB2BF; 4 | 5 | ::selection { 6 | background-color: #9196A1; 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #528BFF; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/predawn.less: -------------------------------------------------------------------------------- 1 | .predawn { 2 | background-color: #282828; 3 | color: #F1F1F1; 4 | 5 | ::selection { 6 | background-color: rgba(255,255,255,0.25); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #F18260; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/pro.less: -------------------------------------------------------------------------------- 1 | .pro { 2 | background-color: black; 3 | color: rgb(244, 244, 244); 4 | 5 | ::selection { 6 | background-color: rgba(82, 82, 82, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(96, 96, 96); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/red-sands.less: -------------------------------------------------------------------------------- 1 | .red-sands { 2 | background-color: rgb(143, 53, 39); 3 | color: rgb(215, 201, 167); 4 | 5 | ::selection { 6 | background-color: rgba(60, 25, 22, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: white; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/red.less: -------------------------------------------------------------------------------- 1 | .red { 2 | background-color: black; 3 | color: rgb(255, 38, 14); 4 | 5 | ::selection { 6 | background-color: rgba(7, 30, 155, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(255, 38, 14); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/silver-aerogel.less: -------------------------------------------------------------------------------- 1 | .silver-aerogel { 2 | background-color: rgb(146, 146, 146); 3 | color: black; 4 | 5 | ::selection { 6 | background-color: rgba(120, 123, 156, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: rgb(224, 224, 224); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/solarized-dark.less: -------------------------------------------------------------------------------- 1 | .solarized-dark { 2 | background-color: #042029; 3 | color: #708284; 4 | 5 | ::selection { 6 | background-color: #839496; 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #819090; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/solarized-light.less: -------------------------------------------------------------------------------- 1 | .solarized-light { 2 | background-color: #FDF6E3; 3 | color: #657A81; 4 | 5 | ::selection { 6 | background-color: #ECE7D5; 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: #586E75; 11 | } 12 | } -------------------------------------------------------------------------------- /styles/themes/solid-colors.less: -------------------------------------------------------------------------------- 1 | .solid-colors { 2 | background-color: rgb(120, 132, 151); 3 | color: black; 4 | 5 | ::selection { 6 | background-color: rgba(178, 215, 255, .99); 7 | } 8 | 9 | .terminal-cursor { 10 | background-color: white; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /styles/themes/standard.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .standard { 4 | background-color: @app-background-color; 5 | color: @text-color; 6 | 7 | .terminal-cursor { 8 | background-color: @text-color-highlight; 9 | } 10 | } 11 | --------------------------------------------------------------------------------