├── .editorconfig ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── BUG-Report.yml └── workflows │ └── ci.yml ├── Documentation ├── Changelog.md ├── Contribute.md ├── Customize.md ├── Keybindings.md └── Settings.md ├── LICENSE ├── README.md ├── Resources ├── Keymap.json ├── Menus.json ├── Screenshots │ └── Preview.gif └── Stylesheet.less ├── Source ├── App.js ├── errors │ └── early-termination-signal.js ├── scroll-markers │ └── scroll-markers-service.js ├── search-model.js ├── selection-manager.js ├── status-bar │ ├── status-bar-service.js │ └── status-bar-view.js └── utils │ ├── editor-finders.js │ ├── escape-reg-exp.js │ ├── is-word-selected.js │ └── non-word-characters.js ├── Tests ├── fixtures │ ├── hex.md │ ├── sample.coffee │ └── sample.php ├── highlight-selected-spec.js ├── main-spec.js ├── scroll-markers │ └── scroll-markers-service-spec.js ├── status-bar │ ├── status-bar-service-spec.js │ └── status-bar-view-spec.js └── utils │ └── non-word-characters-spec.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | 6 | end_of_line = lf 7 | 8 | [*.{gitignore,json,less,yml,js,md}] 9 | 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | charset = utf-8 15 | 16 | [*.md] 17 | 18 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | custom : [ 'https://monzo.me/richrace/3.00?d=For%20a%20coffee,%20thanks%20for%20Highlight%20Selected!%20%F0%9F%8E%89%20%E2%98%95%EF%B8%8F' ] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG-Report.yml: -------------------------------------------------------------------------------- 1 | 2 | description : Create a new ticket for a bug. 3 | title : ❌ Title 4 | name : Bug Report 5 | 6 | labels: 7 | 8 | - bug 9 | 10 | body: 11 | 12 | # Pulsar Version 13 | 14 | - type : textarea 15 | id : pulsar-version 16 | 17 | attributes: 18 | 19 | description : Version of Pulsar, check with `pulsar --version` 20 | label : Pulsar Version 21 | value : | 22 | Pulsar : 1.100.0-beta 23 | Electron : 12.2.3 24 | Chrome : 89.0.4389.128 25 | Node : 14.16.0 26 | 27 | validations: 28 | 29 | required : true 30 | 31 | 32 | # Package Version 33 | 34 | - type : input 35 | id : package-version 36 | 37 | attributes: 38 | 39 | description : Version of Highlight-Line. 40 | placeholder : 0.12.0 41 | label : Package Version 42 | 43 | validations: 44 | 45 | required : true 46 | 47 | 48 | # Description 49 | 50 | - type : textarea 51 | id : description 52 | 53 | attributes: 54 | 55 | description : Please describe your problem as detailed as possible. 56 | label : Description 57 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | 2 | name: CI 3 | 4 | # Disabled as long as the GitHub Action 5 | # is not available / ppm is not properly 6 | # installed. 7 | # 8 | on: 9 | workflow_dispatch: 10 | 11 | # pull_request: 12 | 13 | # push: 14 | # branches: 15 | 16 | # - main 17 | 18 | jobs: 19 | 20 | Test: 21 | 22 | runs-on : ${{ matrix.os }} 23 | if : "!contains(github.event.head_commit.message,'[skip ci]')" 24 | 25 | strategy: 26 | matrix: 27 | channel : [ stable , beta ] 28 | os : [ ubuntu-latest , macos-latest , windows-latest ] 29 | 30 | steps: 31 | 32 | - uses : actions/checkout@v3 33 | 34 | # - uses : pulsar-edit/action-setup-pulsar 35 | # with : 36 | # channel : ${{ matrix.channel }} 37 | 38 | - name : Print Pulsar's Version 39 | run : pulsar -v 40 | 41 | - name : Print PPM's Version 42 | run : ppm -v 43 | 44 | - name : Install Dependencies 45 | run : ppm ci 46 | env : 47 | APM_TEST_PACKAGES : minimap minimap-highlight-selected status-bar 48 | 49 | - name : Run Tests 50 | run : npm run test 51 | 52 | 53 | Skip: 54 | 55 | runs-on : ubuntu-latest 56 | if : "contains(github.event.head_commit.message,'[skip ci]')" 57 | 58 | steps: 59 | 60 | - name : Skip CI 🚫 61 | run : echo skip ci 62 | -------------------------------------------------------------------------------- /Documentation/Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v0.17.0](https://github.com/richrace/highlight-selected/tree/v0.17.0) (2020-01-14) 4 | 5 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.16.0...v0.17.0) 6 | 7 | **Fixed bugs:** 8 | 9 | - Highlight fails on neighbouring words [\#201](https://github.com/richrace/highlight-selected/issues/201) 10 | 11 | **Closed issues:** 12 | 13 | - Uncaught Error: regular expression is too large [\#206](https://github.com/richrace/highlight-selected/issues/206) 14 | - Change Text color of selected word [\#98](https://github.com/richrace/highlight-selected/issues/98) 15 | 16 | **Merged pull requests:** 17 | 18 | - Use GitHub actions [\#211](https://github.com/richrace/highlight-selected/pull/211) ([richrace](https://github.com/richrace)) 19 | - Correctly handle huge selection. [\#210](https://github.com/richrace/highlight-selected/pull/210) ([elarivie](https://github.com/elarivie)) 20 | - Bump eslint-utils from 1.3.1 to 1.4.2 [\#205](https://github.com/richrace/highlight-selected/pull/205) ([dependabot[bot]](https://github.com/apps/dependabot)) 21 | - Bump lodash from 4.17.11 to 4.17.14 [\#204](https://github.com/richrace/highlight-selected/pull/204) ([dependabot[bot]](https://github.com/apps/dependabot)) 22 | - 201 fix regex [\#202](https://github.com/richrace/highlight-selected/pull/202) ([richrace](https://github.com/richrace)) 23 | - Bump js-yaml from 3.12.1 to 3.13.1 [\#200](https://github.com/richrace/highlight-selected/pull/200) ([dependabot[bot]](https://github.com/apps/dependabot)) 24 | 25 | ## [v0.16.0](https://github.com/richrace/highlight-selected/tree/v0.16.0) (2019-03-24) 26 | 27 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.15.0...v0.16.0) 28 | 29 | **Closed issues:** 30 | 31 | - Rewrite to JS [\#175](https://github.com/richrace/highlight-selected/issues/175) 32 | 33 | **Merged pull requests:** 34 | 35 | - Rewrite to ES6 [\#196](https://github.com/richrace/highlight-selected/pull/196) ([richrace](https://github.com/richrace)) 36 | 37 | ## [v0.15.0](https://github.com/richrace/highlight-selected/tree/v0.15.0) (2019-03-17) 38 | 39 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.14.0...v0.15.0) 40 | 41 | **Fixed bugs:** 42 | 43 | - Selected tab and space [\#182](https://github.com/richrace/highlight-selected/issues/182) 44 | 45 | **Closed issues:** 46 | 47 | - Use Grammar Non Word Characters [\#183](https://github.com/richrace/highlight-selected/issues/183) 48 | 49 | **Merged pull requests:** 50 | 51 | - Limit the maximum number of highlight markers [\#193](https://github.com/richrace/highlight-selected/pull/193) ([wbinnssmith](https://github.com/wbinnssmith)) 52 | - Block only whitespace selections [\#190](https://github.com/richrace/highlight-selected/pull/190) ([Arcanemagus](https://github.com/Arcanemagus)) 53 | - Use the grammar's definition of nonWordCharacters [\#189](https://github.com/richrace/highlight-selected/pull/189) ([Arcanemagus](https://github.com/Arcanemagus)) 54 | 55 | ## [v0.14.0](https://github.com/richrace/highlight-selected/tree/v0.14.0) (2018-07-08) 56 | 57 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.13.1...v0.14.0) 58 | 59 | **Implemented enhancements:** 60 | 61 | - Integrate with scroll-marker to highlight matches on the scrollbar [\#173](https://github.com/richrace/highlight-selected/issues/173) 62 | - Doesn't work for unicode words [\#105](https://github.com/richrace/highlight-selected/issues/105) 63 | - Support for same file in multiple panes [\#82](https://github.com/richrace/highlight-selected/issues/82) 64 | 65 | **Fixed bugs:** 66 | 67 | - Uncaught TypeError: Cannot read property 'visibleMarkerLayer' of undefined [\#179](https://github.com/richrace/highlight-selected/issues/179) 68 | - Split pane issues [\#178](https://github.com/richrace/highlight-selected/issues/178) 69 | 70 | **Closed issues:** 71 | 72 | - Update Readme [\#176](https://github.com/richrace/highlight-selected/issues/176) 73 | - Option to highlight one-letter variables [\#170](https://github.com/richrace/highlight-selected/issues/170) 74 | - Uncaught Error: PCRE does not support \L, \l, \N{name}, \U, or \u [\#169](https://github.com/richrace/highlight-selected/issues/169) 75 | - Uncaught Error: PCRE does not support /L, \l, \N{name}, \U, or \u [\#168](https://github.com/richrace/highlight-selected/issues/168) 76 | - Change text when highlighted [\#160](https://github.com/richrace/highlight-selected/issues/160) 77 | - Cyrillic letters [\#157](https://github.com/richrace/highlight-selected/issues/157) 78 | - feature: show highlights in scrollbar [\#155](https://github.com/richrace/highlight-selected/issues/155) 79 | - Chinese character cannot be highlighted [\#153](https://github.com/richrace/highlight-selected/issues/153) 80 | - Uncaught TypeError: escapeRegExp is not a function [\#152](https://github.com/richrace/highlight-selected/issues/152) 81 | - Highlight Background Only on Selected Words [\#151](https://github.com/richrace/highlight-selected/issues/151) 82 | - HighlightedAreaView.onDidAddSelectedMarker is deprecated. [\#149](https://github.com/richrace/highlight-selected/issues/149) 83 | - HighlightedAreaView.onDidAddMarker is deprecated. [\#147](https://github.com/richrace/highlight-selected/issues/147) 84 | - Selection not highlighted when hardware acceleration is disabled [\#146](https://github.com/richrace/highlight-selected/issues/146) 85 | - Whole Words option should support the "$" symbol for JS files [\#141](https://github.com/richrace/highlight-selected/issues/141) 86 | - $variables cannot be highlighted [\#132](https://github.com/richrace/highlight-selected/issues/132) 87 | - Word-boundaries not in line with language settings [\#117](https://github.com/richrace/highlight-selected/issues/117) 88 | - Highlight any selection \(without double-clicking\) [\#113](https://github.com/richrace/highlight-selected/issues/113) 89 | - Missing highlight when line is folded [\#108](https://github.com/richrace/highlight-selected/issues/108) 90 | - Need to highlight variable names starting with $ in Perl as well [\#107](https://github.com/richrace/highlight-selected/issues/107) 91 | - Hightlight status bar shows incorrect num of matches [\#103](https://github.com/richrace/highlight-selected/issues/103) 92 | - Can't select non latin symbols [\#102](https://github.com/richrace/highlight-selected/issues/102) 93 | - Feature Request: Create Cursors On Marked Areas [\#99](https://github.com/richrace/highlight-selected/issues/99) 94 | - Feature request: PHP $ 'special case' should be an option [\#88](https://github.com/richrace/highlight-selected/issues/88) 95 | 96 | **Merged pull requests:** 97 | 98 | - add command to select all markers [\#164](https://github.com/richrace/highlight-selected/pull/164) ([econtal](https://github.com/econtal)) 99 | - Add configuration to adjust the string in the status bar [\#163](https://github.com/richrace/highlight-selected/pull/163) ([fuelingtheweb](https://github.com/fuelingtheweb)) 100 | - Add special case for Perl `$` `%` and `@` variables [\#162](https://github.com/richrace/highlight-selected/pull/162) ([Wigginns](https://github.com/Wigginns)) 101 | - Allow highlighting across word boundaries [\#154](https://github.com/richrace/highlight-selected/pull/154) ([ion201](https://github.com/ion201)) 102 | 103 | ## [v0.13.1](https://github.com/richrace/highlight-selected/tree/v0.13.1) (2017-03-24) 104 | 105 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.13.0...v0.13.1) 106 | 107 | **Closed issues:** 108 | 109 | - Deprecation Notice [\#145](https://github.com/richrace/highlight-selected/issues/145) 110 | - Uncaught TypeError: Cannot read property 'scanInBufferRange' of undefined [\#127](https://github.com/richrace/highlight-selected/issues/127) 111 | 112 | **Merged pull requests:** 113 | 114 | - Optimize load time [\#148](https://github.com/richrace/highlight-selected/pull/148) ([zertosh](https://github.com/zertosh)) 115 | 116 | ## [v0.13.0](https://github.com/richrace/highlight-selected/tree/v0.13.0) (2017-03-23) 117 | 118 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.12.0...v0.13.0) 119 | 120 | **Closed issues:** 121 | 122 | - Failed to activate the highlight-selected package [\#142](https://github.com/richrace/highlight-selected/issues/142) 123 | - Uncaught TypeError: Cannot read property 'push' of undefined [\#140](https://github.com/richrace/highlight-selected/issues/140) 124 | - Deprecated selector in `highlight-selected\styles\highlight-selected.less` [\#134](https://github.com/richrace/highlight-selected/issues/134) 125 | - Deprecated selector in `highlight-selected/styles/highlight-selected.less` [\#131](https://github.com/richrace/highlight-selected/issues/131) 126 | 127 | **Merged pull requests:** 128 | 129 | - Add HACK grammar to $ special handling [\#143](https://github.com/richrace/highlight-selected/pull/143) ([zertosh](https://github.com/zertosh)) 130 | - Add a Gitter chat badge to README.md [\#86](https://github.com/richrace/highlight-selected/pull/86) ([gitter-badger](https://github.com/gitter-badger)) 131 | 132 | ## [v0.12.0](https://github.com/richrace/highlight-selected/tree/v0.12.0) (2017-01-11) 133 | 134 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.11.2...v0.12.0) 135 | 136 | **Closed issues:** 137 | 138 | - Deprecation warnings in Atom 1.13.0 [\#138](https://github.com/richrace/highlight-selected/issues/138) 139 | - Can't change selection "shading" color, only the border color [\#137](https://github.com/richrace/highlight-selected/issues/137) 140 | - Feature request: Highlight selected word on mini-map [\#135](https://github.com/richrace/highlight-selected/issues/135) 141 | - if the view is splict to left and right, possible to highlight right when click left [\#123](https://github.com/richrace/highlight-selected/issues/123) 142 | - Uncaught TypeError: Failed to execute 'item' on 'HTMLCollection': 1 argument required, but only 0 present. [\#122](https://github.com/richrace/highlight-selected/issues/122) 143 | - Crashes when pointer position is changed [\#121](https://github.com/richrace/highlight-selected/issues/121) 144 | - High startup time [\#118](https://github.com/richrace/highlight-selected/issues/118) 145 | - \[Suggestion\] Match only whole word [\#116](https://github.com/richrace/highlight-selected/issues/116) 146 | - Mouse button release not being detected [\#114](https://github.com/richrace/highlight-selected/issues/114) 147 | - Performance on large files [\#104](https://github.com/richrace/highlight-selected/issues/104) 148 | - Highlight only when match found [\#90](https://github.com/richrace/highlight-selected/issues/90) 149 | - How to add a keybinding to highlight the word under the cursor? [\#87](https://github.com/richrace/highlight-selected/issues/87) 150 | - Failed to activate the remember-session package [\#55](https://github.com/richrace/highlight-selected/issues/55) 151 | 152 | **Merged pull requests:** 153 | 154 | - Retire :host and ::shadow pseudo-selectors [\#136](https://github.com/richrace/highlight-selected/pull/136) ([pyrotechnick](https://github.com/pyrotechnick)) 155 | - Switches to Marker Layers API [\#128](https://github.com/richrace/highlight-selected/pull/128) ([dbachko](https://github.com/dbachko)) 156 | - Highlight selection across active panes [\#126](https://github.com/richrace/highlight-selected/pull/126) ([mswiszcz](https://github.com/mswiszcz)) 157 | 158 | ## [v0.11.2](https://github.com/richrace/highlight-selected/tree/v0.11.2) (2016-02-02) 159 | 160 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.11.1...v0.11.2) 161 | 162 | **Closed issues:** 163 | 164 | - High CPU usage [\#110](https://github.com/richrace/highlight-selected/issues/110) 165 | - Highlight Line Numbers also when selected \( enhancement \) [\#76](https://github.com/richrace/highlight-selected/issues/76) 166 | 167 | **Merged pull requests:** 168 | 169 | - Added inline-block class for better compatibility [\#106](https://github.com/richrace/highlight-selected/pull/106) ([scippio](https://github.com/scippio)) 170 | 171 | ## [v0.11.1](https://github.com/richrace/highlight-selected/tree/v0.11.1) (2015-11-15) 172 | 173 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.11.0...v0.11.1) 174 | 175 | ## [v0.11.0](https://github.com/richrace/highlight-selected/tree/v0.11.0) (2015-11-15) 176 | 177 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.10.1...v0.11.0) 178 | 179 | **Implemented enhancements:** 180 | 181 | - Counter for highlighted words [\#94](https://github.com/richrace/highlight-selected/issues/94) 182 | - Command to toggle highlight selected [\#92](https://github.com/richrace/highlight-selected/issues/92) 183 | - Add API [\#63](https://github.com/richrace/highlight-selected/issues/63) 184 | 185 | **Closed issues:** 186 | 187 | - Plugin should only select fully matching items [\#101](https://github.com/richrace/highlight-selected/issues/101) 188 | - Don't highlight partial matches [\#95](https://github.com/richrace/highlight-selected/issues/95) 189 | - Status bar number of highlight/occurrences is one off [\#93](https://github.com/richrace/highlight-selected/issues/93) 190 | - I cannot install this package [\#91](https://github.com/richrace/highlight-selected/issues/91) 191 | - Only highlight occurences in lines below/above selected word [\#89](https://github.com/richrace/highlight-selected/issues/89) 192 | - Uncaught TypeError: Cannot read property 'dispose' of undefined [\#85](https://github.com/richrace/highlight-selected/issues/85) 193 | 194 | **Merged pull requests:** 195 | 196 | - Add Highlighted info to the status bar. Closes \#94 [\#100](https://github.com/richrace/highlight-selected/pull/100) ([richrace](https://github.com/richrace)) 197 | - Add basic API [\#81](https://github.com/richrace/highlight-selected/pull/81) ([richrace](https://github.com/richrace)) 198 | 199 | ## [v0.10.1](https://github.com/richrace/highlight-selected/tree/v0.10.1) (2015-06-17) 200 | 201 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.10.0...v0.10.1) 202 | 203 | **Fixed bugs:** 204 | 205 | - Not closing the bottom/top of the highlight on line 144/145 [\#84](https://github.com/richrace/highlight-selected/issues/84) 206 | 207 | **Closed issues:** 208 | 209 | - Highlight with one click [\#83](https://github.com/richrace/highlight-selected/issues/83) 210 | 211 | ## [v0.10.0](https://github.com/richrace/highlight-selected/tree/v0.10.0) (2015-06-14) 212 | 213 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.9.3...v0.10.0) 214 | 215 | **Implemented enhancements:** 216 | 217 | - Only highlight after timeout [\#75](https://github.com/richrace/highlight-selected/issues/75) 218 | 219 | **Closed issues:** 220 | 221 | - Highlight not always working with double click to select word [\#77](https://github.com/richrace/highlight-selected/issues/77) 222 | - Can not use cmd key to multi-select content [\#74](https://github.com/richrace/highlight-selected/issues/74) 223 | - Whole Words option should support the "$" symbol for PHP files [\#73](https://github.com/richrace/highlight-selected/issues/73) 224 | - can't see which ones are selected [\#71](https://github.com/richrace/highlight-selected/issues/71) 225 | - An error message shows up with no obvious abnormal behavior [\#70](https://github.com/richrace/highlight-selected/issues/70) 226 | - Uncaught TypeError: Cannot read property 'dispose' of undefined [\#69](https://github.com/richrace/highlight-selected/issues/69) 227 | 228 | **Merged pull requests:** 229 | 230 | - Add special case for PHP `$` variables [\#80](https://github.com/richrace/highlight-selected/pull/80) ([richrace](https://github.com/richrace)) 231 | - Add debounce feature [\#79](https://github.com/richrace/highlight-selected/pull/79) ([richrace](https://github.com/richrace)) 232 | 233 | ## [v0.9.3](https://github.com/richrace/highlight-selected/tree/v0.9.3) (2015-05-15) 234 | 235 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.9.2...v0.9.3) 236 | 237 | **Closed issues:** 238 | 239 | - Uncaught TypeError: Cannot read property 'dispose' of undefined [\#67](https://github.com/richrace/highlight-selected/issues/67) 240 | - Failed to load the highlight-selected package [\#65](https://github.com/richrace/highlight-selected/issues/65) 241 | - Uncaught TypeError: undefined is not a function [\#61](https://github.com/richrace/highlight-selected/issues/61) 242 | 243 | **Merged pull requests:** 244 | 245 | - Remove dependency on 'atom-space-pen-views' [\#78](https://github.com/richrace/highlight-selected/pull/78) ([jccr](https://github.com/jccr)) 246 | - fix \#67 [\#68](https://github.com/richrace/highlight-selected/pull/68) ([yongkangchen](https://github.com/yongkangchen)) 247 | - Update README.md [\#66](https://github.com/richrace/highlight-selected/pull/66) ([simon-clay](https://github.com/simon-clay)) 248 | - Test minimap dependencies [\#62](https://github.com/richrace/highlight-selected/pull/62) ([richrace](https://github.com/richrace)) 249 | 250 | ## [v0.9.2](https://github.com/richrace/highlight-selected/tree/v0.9.2) (2015-04-18) 251 | 252 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.9.1...v0.9.2) 253 | 254 | **Fixed bugs:** 255 | 256 | - Hide highlight on multiselect [\#56](https://github.com/richrace/highlight-selected/issues/56) 257 | 258 | **Closed issues:** 259 | 260 | - Only highlight matches when they are whole words [\#59](https://github.com/richrace/highlight-selected/issues/59) 261 | - Module fails to activate [\#52](https://github.com/richrace/highlight-selected/issues/52) 262 | 263 | **Merged pull requests:** 264 | 265 | - Fix \#56 [\#58](https://github.com/richrace/highlight-selected/pull/58) ([hmatsuda](https://github.com/hmatsuda)) 266 | - update link reference [\#57](https://github.com/richrace/highlight-selected/pull/57) ([nobitagit](https://github.com/nobitagit)) 267 | 268 | ## [v0.9.1](https://github.com/richrace/highlight-selected/tree/v0.9.1) (2015-03-01) 269 | 270 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.9.0...v0.9.1) 271 | 272 | **Implemented enhancements:** 273 | 274 | - Minimum length [\#36](https://github.com/richrace/highlight-selected/issues/36) 275 | 276 | **Closed issues:** 277 | 278 | - hide panel on startup [\#54](https://github.com/richrace/highlight-selected/issues/54) 279 | - Atom.Object.defineProperty.get is deprecated. [\#50](https://github.com/richrace/highlight-selected/issues/50) 280 | - Styling has no effect on highlighting [\#41](https://github.com/richrace/highlight-selected/issues/41) 281 | 282 | **Merged pull requests:** 283 | 284 | - Update README example [\#51](https://github.com/richrace/highlight-selected/pull/51) ([tylerdiaz](https://github.com/tylerdiaz)) 285 | 286 | ## [v0.9.0](https://github.com/richrace/highlight-selected/tree/v0.9.0) (2015-02-13) 287 | 288 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.8.0...v0.9.0) 289 | 290 | **Closed issues:** 291 | 292 | - Atom.Object.defineProperty.get is deprecated. [\#47](https://github.com/richrace/highlight-selected/issues/47) 293 | - Atom.Object.defineProperty.get is deprecated. [\#45](https://github.com/richrace/highlight-selected/issues/45) 294 | - Atom.Object.defineProperty.get is deprecated. [\#44](https://github.com/richrace/highlight-selected/issues/44) 295 | - Deprecated Selectors [\#42](https://github.com/richrace/highlight-selected/issues/42) 296 | - Atom.Object.defineProperty.get is deprecated. [\#40](https://github.com/richrace/highlight-selected/issues/40) 297 | - Atom.Object.defineProperty.get is deprecated. [\#39](https://github.com/richrace/highlight-selected/issues/39) 298 | - Atom.Object.defineProperty.get is deprecated. [\#38](https://github.com/richrace/highlight-selected/issues/38) 299 | - Atom.Object.defineProperty.get is deprecated. [\#37](https://github.com/richrace/highlight-selected/issues/37) 300 | - Atom.Object.defineProperty.get is deprecated. [\#34](https://github.com/richrace/highlight-selected/issues/34) 301 | - Use new Events API [\#29](https://github.com/richrace/highlight-selected/issues/29) 302 | 303 | **Merged pull requests:** 304 | 305 | - Add minimum length option [\#49](https://github.com/richrace/highlight-selected/pull/49) ([hmatsuda](https://github.com/hmatsuda)) 306 | - Fix atom engine semver [\#48](https://github.com/richrace/highlight-selected/pull/48) ([izuzak](https://github.com/izuzak)) 307 | 308 | ## [v0.8.0](https://github.com/richrace/highlight-selected/tree/v0.8.0) (2015-02-02) 309 | 310 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.7.0...v0.8.0) 311 | 312 | **Closed issues:** 313 | 314 | - Atom.Object.defineProperty.get is deprecated. [\#35](https://github.com/richrace/highlight-selected/issues/35) 315 | 316 | **Merged pull requests:** 317 | 318 | - Upgrade this package to 1.0 APIs [\#46](https://github.com/richrace/highlight-selected/pull/46) ([hmatsuda](https://github.com/hmatsuda)) 319 | 320 | ## [v0.7.0](https://github.com/richrace/highlight-selected/tree/v0.7.0) (2014-12-17) 321 | 322 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.6.3...v0.7.0) 323 | 324 | **Closed issues:** 325 | 326 | - Styles needs an update to support new shadow DOM setting [\#30](https://github.com/richrace/highlight-selected/issues/30) 327 | - Better colors for light theme [\#26](https://github.com/richrace/highlight-selected/issues/26) 328 | 329 | **Merged pull requests:** 330 | 331 | - Fix \#30 [\#33](https://github.com/richrace/highlight-selected/pull/33) ([yongkangchen](https://github.com/yongkangchen)) 332 | 333 | ## [v0.6.3](https://github.com/richrace/highlight-selected/tree/v0.6.3) (2014-11-20) 334 | 335 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.6.2...v0.6.3) 336 | 337 | **Closed issues:** 338 | 339 | - option of hideHighlightOnSelectedWord sometimes not work [\#31](https://github.com/richrace/highlight-selected/issues/31) 340 | - Highlights incorrectly positioned [\#28](https://github.com/richrace/highlight-selected/issues/28) 341 | 342 | **Merged pull requests:** 343 | 344 | - fix hideHighlightOnSelectedWord sometimes not work [\#32](https://github.com/richrace/highlight-selected/pull/32) ([yongkangchen](https://github.com/yongkangchen)) 345 | 346 | ## [v0.6.2](https://github.com/richrace/highlight-selected/tree/v0.6.2) (2014-09-20) 347 | 348 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.6.1...v0.6.2) 349 | 350 | **Merged pull requests:** 351 | 352 | - implement better colors for light theme [\#27](https://github.com/richrace/highlight-selected/pull/27) ([Bengt](https://github.com/Bengt)) 353 | 354 | ## [v0.6.1](https://github.com/richrace/highlight-selected/tree/v0.6.1) (2014-09-15) 355 | 356 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.6.0...v0.6.1) 357 | 358 | **Closed issues:** 359 | 360 | - Editor.getSelection is deprecated. [\#25](https://github.com/richrace/highlight-selected/issues/25) 361 | - Package broken on commit be1d4ee5dc7c125bcd7ffb0f97af6414bcba6e7d of atom [\#23](https://github.com/richrace/highlight-selected/issues/23) 362 | - Ignore 1-char selections [\#22](https://github.com/richrace/highlight-selected/issues/22) 363 | 364 | ## [v0.6.0](https://github.com/richrace/highlight-selected/tree/v0.6.0) (2014-08-23) 365 | 366 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.5...v0.6.0) 367 | 368 | ## [v0.5.5](https://github.com/richrace/highlight-selected/tree/v0.5.5) (2014-08-22) 369 | 370 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.4...v0.5.5) 371 | 372 | **Closed issues:** 373 | 374 | - Leading whitespace [\#21](https://github.com/richrace/highlight-selected/issues/21) 375 | - Highlight Background appears twice in settings [\#20](https://github.com/richrace/highlight-selected/issues/20) 376 | 377 | ## [v0.5.4](https://github.com/richrace/highlight-selected/tree/v0.5.4) (2014-08-19) 378 | 379 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.3...v0.5.4) 380 | 381 | ## [v0.5.3](https://github.com/richrace/highlight-selected/tree/v0.5.3) (2014-08-18) 382 | 383 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.2...v0.5.3) 384 | 385 | ## [v0.5.2](https://github.com/richrace/highlight-selected/tree/v0.5.2) (2014-08-18) 386 | 387 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.1...v0.5.2) 388 | 389 | **Closed issues:** 390 | 391 | - Feature request: highlight words in full color, not only with canvas. [\#15](https://github.com/richrace/highlight-selected/issues/15) 392 | - The highlighted words are almost invisible in light theme [\#14](https://github.com/richrace/highlight-selected/issues/14) 393 | 394 | ## [v0.5.1](https://github.com/richrace/highlight-selected/tree/v0.5.1) (2014-08-18) 395 | 396 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.5.0...v0.5.1) 397 | 398 | ## [v0.5.0](https://github.com/richrace/highlight-selected/tree/v0.5.0) (2014-08-17) 399 | 400 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.4.1...v0.5.0) 401 | 402 | **Closed issues:** 403 | 404 | - Use decorations [\#17](https://github.com/richrace/highlight-selected/issues/17) 405 | - Having highlight-selected installed breaks Atom specs [\#16](https://github.com/richrace/highlight-selected/issues/16) 406 | - If I edit one occurence of the highlighted word it should edit the others too [\#13](https://github.com/richrace/highlight-selected/issues/13) 407 | - Poor performance with large files [\#3](https://github.com/richrace/highlight-selected/issues/3) 408 | 409 | **Merged pull requests:** 410 | 411 | - Decorations [\#19](https://github.com/richrace/highlight-selected/pull/19) ([richrace](https://github.com/richrace)) 412 | - Add Ignore Case Option [\#18](https://github.com/richrace/highlight-selected/pull/18) ([andreldm](https://github.com/andreldm)) 413 | 414 | ## [v0.4.1](https://github.com/richrace/highlight-selected/tree/v0.4.1) (2014-07-19) 415 | 416 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.4.0...v0.4.1) 417 | 418 | **Implemented enhancements:** 419 | 420 | - Consider current selected don't have border [\#11](https://github.com/richrace/highlight-selected/issues/11) 421 | 422 | **Closed issues:** 423 | 424 | - Uncaught TypeError: Cannot call method 'pixelPositionForScreenPosition' of null -React- [\#10](https://github.com/richrace/highlight-selected/issues/10) 425 | 426 | ## [v0.4.0](https://github.com/richrace/highlight-selected/tree/v0.4.0) (2014-07-03) 427 | 428 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.3.0...v0.4.0) 429 | 430 | **Closed issues:** 431 | 432 | - Cannot call method 'pixelPositionForScreenPosition' of null when using React editor [\#8](https://github.com/richrace/highlight-selected/issues/8) 433 | 434 | **Merged pull requests:** 435 | 436 | - React Editor pixelPositionForScreenPosition fix [\#9](https://github.com/richrace/highlight-selected/pull/9) ([taylorludwig](https://github.com/taylorludwig)) 437 | 438 | ## [v0.3.0](https://github.com/richrace/highlight-selected/tree/v0.3.0) (2014-06-29) 439 | 440 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.11...v0.3.0) 441 | 442 | **Merged pull requests:** 443 | 444 | - fix\(marker-view.coffee\): on Windows, lineHeight undefined. [\#6](https://github.com/richrace/highlight-selected/pull/6) ([nickeddy](https://github.com/nickeddy)) 445 | 446 | ## [v0.2.11](https://github.com/richrace/highlight-selected/tree/v0.2.11) (2014-04-06) 447 | 448 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.10...v0.2.11) 449 | 450 | ## [v0.2.10](https://github.com/richrace/highlight-selected/tree/v0.2.10) (2014-04-06) 451 | 452 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.9...v0.2.10) 453 | 454 | **Closed issues:** 455 | 456 | - Only highlight whole words [\#5](https://github.com/richrace/highlight-selected/issues/5) 457 | 458 | ## [v0.2.9](https://github.com/richrace/highlight-selected/tree/v0.2.9) (2014-03-22) 459 | 460 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.8...v0.2.9) 461 | 462 | **Closed issues:** 463 | 464 | - Match "$" and "@" [\#4](https://github.com/richrace/highlight-selected/issues/4) 465 | 466 | ## [v0.2.8](https://github.com/richrace/highlight-selected/tree/v0.2.8) (2014-03-15) 467 | 468 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.7...v0.2.8) 469 | 470 | ## [v0.2.7](https://github.com/richrace/highlight-selected/tree/v0.2.7) (2014-03-15) 471 | 472 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.6...v0.2.7) 473 | 474 | ## [v0.2.6](https://github.com/richrace/highlight-selected/tree/v0.2.6) (2014-03-13) 475 | 476 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.5...v0.2.6) 477 | 478 | ## [v0.2.5](https://github.com/richrace/highlight-selected/tree/v0.2.5) (2014-03-05) 479 | 480 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.4...v0.2.5) 481 | 482 | ## [v0.2.4](https://github.com/richrace/highlight-selected/tree/v0.2.4) (2014-03-05) 483 | 484 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.3...v0.2.4) 485 | 486 | **Fixed bugs:** 487 | 488 | - Problem with multiple panes open [\#2](https://github.com/richrace/highlight-selected/issues/2) 489 | - Strange offset with highlight [\#1](https://github.com/richrace/highlight-selected/issues/1) 490 | 491 | ## [v0.2.3](https://github.com/richrace/highlight-selected/tree/v0.2.3) (2014-03-05) 492 | 493 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.2...v0.2.3) 494 | 495 | ## [v0.2.2](https://github.com/richrace/highlight-selected/tree/v0.2.2) (2014-03-04) 496 | 497 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.1...v0.2.2) 498 | 499 | ## [v0.2.1](https://github.com/richrace/highlight-selected/tree/v0.2.1) (2014-03-04) 500 | 501 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.2.0...v0.2.1) 502 | 503 | ## [v0.2.0](https://github.com/richrace/highlight-selected/tree/v0.2.0) (2014-03-04) 504 | 505 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.1.3...v0.2.0) 506 | 507 | ## [v0.1.3](https://github.com/richrace/highlight-selected/tree/v0.1.3) (2014-03-03) 508 | 509 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.1.2...v0.1.3) 510 | 511 | ## [v0.1.2](https://github.com/richrace/highlight-selected/tree/v0.1.2) (2014-03-02) 512 | 513 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.1.1...v0.1.2) 514 | 515 | ## [v0.1.1](https://github.com/richrace/highlight-selected/tree/v0.1.1) (2014-03-02) 516 | 517 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/v0.1.0...v0.1.1) 518 | 519 | ## [v0.1.0](https://github.com/richrace/highlight-selected/tree/v0.1.0) (2014-03-02) 520 | 521 | [Full Changelog](https://github.com/richrace/highlight-selected/compare/6a35111b27d087957b6ec9d0fa56c6b095c11fb2...v0.1.0) 522 | 523 | 524 | 525 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 526 | -------------------------------------------------------------------------------- /Documentation/Contribute.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | Before you open a pull request, make sure to have all tests pass. 5 | 6 |
7 | 8 | ## Running Tests 9 | 10 | Tests can be run with: 11 | 12 | ```sh 13 | npm run test 14 | ``` 15 | 16 |
17 | -------------------------------------------------------------------------------- /Documentation/Customize.md: -------------------------------------------------------------------------------- 1 | 2 | ## Styling 3 | 4 | If you want to change any of the styling of the region use the following as a guide: 5 | 6 | ```scss 7 | atom-text-editor .highlights { 8 | // Box 9 | .highlight-selected .region { 10 | border-color: #ddd; 11 | border-radius: 3px; 12 | border-width: 1px; 13 | border-style: solid; 14 | } 15 | // Background (set in settings) 16 | .highlight-selected.background .region { 17 | background-color: rgba(155, 149, 0, 0.6); 18 | } 19 | // Light theme box (set in settings) 20 | .highlight-selected.light-theme .region { 21 | border-color: rgba(255, 128, 64, 0.4); 22 | } 23 | // Light theme background (set in settings) 24 | .highlight-selected.light-theme.background .region { 25 | background-color: rgba(255, 128, 64, 0.2); 26 | } 27 | } 28 | 29 | // If you have the Scroll Marker package installed https://atom.io/packages/scroll-marker 30 | // These are the colours that will be shown in the scroller 31 | .highlight-selected-marker-layer.scroll-marker-layer { 32 | .scroll-marker { 33 | background-color: #ffff00; 34 | } 35 | } 36 | 37 | .highlight-selected-selected-marker-layer.scroll-marker-layer { 38 | .scroll-marker { 39 | background-color: #f71010; 40 | } 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /Documentation/Keybindings.md: -------------------------------------------------------------------------------- 1 | 2 | ## Commands 3 | 4 | | Command Name | Command Code | Keymap | Description | 5 | | ------------------ | ------------------------------- | ------------------------------------------- | ----------------------------- | 6 | | Toggle | `highlight-selected:toggle` | ctrl+cmd+h | Enables/Disabled this package | 7 | | Select all markers | `highlight-selected:select-all` | | Select all markers | 8 | 9 | To set a Keymap for select all open your `Keymap` file and add: 10 | 11 | ```coffeescript 12 | 'atom-text-editor:not([mini])': 13 | 'cmd-*': 'highlight-selected:select-all' 14 | ``` 15 | -------------------------------------------------------------------------------- /Documentation/Settings.md: -------------------------------------------------------------------------------- 1 | 2 | # Settings 3 | 4 |
5 | 6 | ### Only Highlight Whole Words 7 | 8 | Default : `true` 9 | 10 |
11 | 12 | This uses the `Allowed Characters To Select` option 13 | with Pulsar's `Non-word Characters` to find words. 14 | 15 |
16 |
17 | 18 | ### Hide Highlight On Selected Word 19 | 20 | Default : `false` 21 | 22 |
23 | 24 | Only highlights occurrences, not the selected word. 25 | 26 |
27 |
28 | 29 | ### Ignore Case 30 | 31 | Default : `false` 32 | 33 |
34 | 35 | Matches words even if the case isn't the same. 36 | 37 |
38 |
39 | 40 | ### Light Theme 41 | 42 | Default : `false` 43 | 44 |
45 | 46 | Uses the light theme styling described in [`Customize.md`]. 47 | 48 |
49 |
50 | 51 | ### Highlight Background 52 | 53 | Default : `false` 54 | 55 |
56 | 57 | Highlights the background of the matched occurrences. 58 | 59 |
60 |
61 | 62 | ### Minimum Length 63 | 64 | Default : `2` 65 | 66 |
67 | 68 | Minimum length of chars you need to select 69 | before other occurrences are highlighted. 70 | 71 |
72 |
73 | 74 | ### Timeout 75 | 76 | Default : `20` 77 | 78 |
79 | 80 | Stop searching after the given amount of milliseconds. 81 | 82 |
83 |
84 | 85 | ### Highlight In Panes 86 | 87 | Default : `true` 88 | 89 |
90 | 91 | Also highlight in other editor panes. 92 | 93 |
94 |
95 | 96 | ### Show In Status Bar 97 | 98 | Default : `true` 99 | 100 |
101 | 102 | Display the amount of matched 103 | occurrences in the status bar. 104 | 105 |
106 |
107 | 108 | ### Status Bar String 109 | 110 | Default : `Highlighted: %c` 111 | 112 |
113 | 114 | The template for the text shown in the status bar, 115 | at `%c` the number of occurrences is inserted. 116 | 117 |
118 |
119 | 120 | ### Allowed Characters To Select 121 | 122 | Default : `\$@%-` 123 | 124 |
125 | 126 | Non word characters that will be matched in your selection. 127 | 128 | This can be useful in languages like PHP 129 | where variables names start with `$`. 130 | 131 |
132 |
133 | 134 | ### Show Results On Scroll Bar 135 | 136 | Default : `false` 137 | 138 |
139 | 140 | Displays highlighted sections on the scroll bar. 141 | 142 | This requires the **[Scroll Marker]** package. 143 | 144 |
145 | 146 | 147 | 148 | 149 | [`Customize.md`]: Customize.md 150 | 151 | [Scroll Marker]: https://web.pulsar-edit.dev/packages/scroll-marker 152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2014 Richard Race 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | [![Badge Version]][Releases]   7 | [![Badge License]][License] 8 | 9 | 10 | 11 |
12 |
13 | 14 | # Highlight Selected 15 | 16 | *Pulsar package that highlights other* 17 | *occurrences of your current selection.* 18 | 19 |
20 |
21 | 22 | [![Button Customize]][Customize]   23 | [![Button KeyBindings]][KeyBindings]   24 | [![Button Settings]][Settings] 25 | 26 | [![Button Contribute]][Contribute]   27 | [![Button Changelog]][Changelog] 28 | 29 |
30 |
31 | 32 |
33 | 34 | [![Preview]][#] 35 | 36 |
37 | 38 | 39 | 40 | [Releases]: https://github.com/Pulsar-Edit-Highlights/selected/releases 41 | [Package]: https://web.pulsar-edit.dev/packages/highlight-selected 42 | [Actions]: https://github.com/Pulsar-Edit-Highlights/selected/actions 43 | 44 | [Preview]: Resources/Screenshots/Preview.gif 45 | [KeyBindings]: Documentation/KeyBindings.md 46 | [Contribute]: Documentation/Contribute.md 47 | [Changelog]: Documentation/Changelog.md 48 | [Customize]: Documentation/Customize.md 49 | [Settings]: Documentation/Settings.md 50 | [License]: LICENSE 51 | 52 | [#]: # 53 | 54 | 55 | 56 | 57 | [Badge License]: https://img.shields.io/badge/License-MIT-ac8b11.svg?style=for-the-badge&labelColor=yellow&logo=GitBook&logoColor=white 58 | [Badge Version]: https://img.shields.io/github/package-json/v/Pulsar-Edit-Highlights/selected?style=for-the-badge&logo=BookStack&logoColor=white&labelColor=609926&color=4e7a1e 59 | [Badge CI]: https://img.shields.io/github/actions/workflow/status/Pulsar-Edit-Highlights/selected/ci.yml?style=for-the-badge&logo=GitHubActions&logoColor=white&color=582c6d&labelColor=73398D 60 | 61 | 62 | 63 | 64 | [Button KeyBindings]: https://img.shields.io/badge/KeyBindings-3499CD?style=for-the-badge&logoColor=white&logo=AppleArcade 65 | [Button Contribute]: https://img.shields.io/badge/Contribute-7952B3?style=for-the-badge&logoColor=white&logo=GitHub 66 | [Button Changelog]: https://img.shields.io/badge/Changelog-37814A?style=for-the-badge&logoColor=white&logo=GitLFS 67 | [Button Customize]: https://img.shields.io/badge/Customize-00979D?style=for-the-badge&logoColor=white&logo=Rainmeter 68 | [Button Settings]: https://img.shields.io/badge/Settings-yellow?style=for-the-badge&logoColor=white&logo=AzureArtifacts 69 | -------------------------------------------------------------------------------- /Resources/Keymap.json: -------------------------------------------------------------------------------- 1 | { 2 | "atom-text-editor" : { 3 | "ctrl-cmd-h" : "highlight-selected:toggle" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Resources/Menus.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu" : [{ 3 | 4 | "label" : "Packages" , 5 | 6 | "submenu" : [{ 7 | 8 | "label" : "Highlight Selected" , 9 | 10 | "submenu" : [{ 11 | "command" : "highlight-selected:toggle" , 12 | "label" : "Toggle" 13 | }] 14 | }] 15 | }] 16 | } 17 | -------------------------------------------------------------------------------- /Resources/Screenshots/Preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pulsar-Edit-Highlights/selected/1f940a0588cdd84aaee576d864516f264183fec5/Resources/Screenshots/Preview.gif -------------------------------------------------------------------------------- /Resources/Stylesheet.less: -------------------------------------------------------------------------------- 1 | 2 | @import 'ui-variables'; 3 | 4 | 5 | atom-text-editor .highlights { 6 | 7 | .highlight.highlight-selected .region { 8 | 9 | background-color : transparent ; 10 | pointer-events : none ; 11 | box-sizing : border-box ; 12 | position : absolute ; 13 | z-index : -1 ; 14 | } 15 | 16 | 17 | // Box 18 | 19 | .highlight-selected .region { 20 | 21 | border-radius : 3px ; 22 | border-color : #ddd ; 23 | border-style : solid ; 24 | border-width : 1px ; 25 | } 26 | 27 | 28 | /* 29 | * Background 30 | * ( Set in settings ) 31 | */ 32 | 33 | .highlight-selected.background .region { 34 | background-color : rgba( 155 , 149 , 0 , 0.6 ) ; 35 | } 36 | 37 | 38 | /* 39 | * Light Theme Box 40 | * ( Set in settings ) 41 | */ 42 | 43 | .highlight-selected.light-theme .region { 44 | border-color : rgba( 255 , 128 , 64 , 0.4 ) ; 45 | } 46 | 47 | 48 | /* 49 | * Light Theme Background 50 | * ( Set in settings ) 51 | */ 52 | 53 | .highlight-selected.light-theme.background .region { 54 | background-color : rgba( 255 , 128 , 64 , 0.2 ) ; 55 | } 56 | } 57 | 58 | 59 | status-bar { 60 | 61 | .highlight-selected-status { 62 | display : inline-block ; 63 | } 64 | 65 | .highlight-selected-hidden { 66 | display : none ; 67 | } 68 | } 69 | 70 | 71 | .highlight-selected-marker-layer.scroll-marker-layer { 72 | 73 | .scroll-marker { 74 | background-color : #ffff00 ; 75 | } 76 | } 77 | 78 | 79 | .highlight-selected-selected-marker-layer.scroll-marker-layer { 80 | 81 | .scroll-marker { 82 | background-color : #f71010 ; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Source/App.js: -------------------------------------------------------------------------------- 1 | const { CompositeDisposable } = require('atom'); 2 | const ScrollMarkersService = require('./scroll-markers/scroll-markers-service'); 3 | const StatusBarService = require('./status-bar/status-bar-service'); 4 | const SelectionManager = require('./selection-manager'); 5 | 6 | 7 | const { commands } = atom; 8 | 9 | 10 | module.exports = { 11 | 12 | selectionManager: null, 13 | 14 | activate (){ 15 | 16 | this.selectionManager = new SelectionManager; 17 | this.subscriptions = new CompositeDisposable; 18 | 19 | this.subscriptions.add(this.listenForCommands()); 20 | 21 | this.scrollMarkersService = new ScrollMarkersService(this.selectionManager); 22 | }, 23 | 24 | deactivate (){ 25 | 26 | this.scrollMarkersService?.destroy(); 27 | this.selectionManager?.destroy(); 28 | this.statusBarService?.destroy(); 29 | this.subscriptions?.dispose(); 30 | 31 | this.scrollMarkersService = null; 32 | this.selectionManager = null; 33 | this.statusBarService = null; 34 | this.subscriptions = null; 35 | }, 36 | 37 | provideHighlightSelectedV1Deprecated (){ 38 | return this.selectionManager 39 | }, 40 | 41 | provideHighlightSelectedV2 (){ 42 | return this.selectionManager 43 | }, 44 | 45 | consumeStatusBar(statusBar){ 46 | this.statusBarService = 47 | new StatusBarService(statusBar,this.selectionManager); 48 | }, 49 | 50 | toggle (){ 51 | return (this.selectionManager.disabled) 52 | ? this.selectionManager.enable() 53 | : this.selectionManager.disable() 54 | }, 55 | 56 | selectAll (){ 57 | return this.selectionManager.selectAll() 58 | }, 59 | 60 | consumeScrollMarker ( scrollMarkerAPI ){ 61 | this.scrollMarkersService 62 | .setScrollMarkerAPI(scrollMarkerAPI); 63 | }, 64 | 65 | listenForCommands (){ 66 | return commands.add('atom-workspace',{ 67 | 'highlight-selected:select-all': () => this.selectAll() , 68 | 'highlight-selected:toggle': () => this.toggle() 69 | }) 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /Source/errors/early-termination-signal.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | class EarlyTerminationSignal extends Error {} 4 | 5 | 6 | module.exports = EarlyTerminationSignal; 7 | -------------------------------------------------------------------------------- /Source/scroll-markers/scroll-markers-service.js: -------------------------------------------------------------------------------- 1 | 2 | const { CompositeDisposable } = require('atom'); 3 | 4 | 5 | const { workspace , config } = atom; 6 | 7 | 8 | 9 | const showResultsOnScrollbar = () => 10 | config.get('highlight-selected.showResultsOnScrollBar'); 11 | 12 | 13 | 14 | module.exports = class ScrollMarkersService { 15 | 16 | static ensureScrollViewInstalled (){ 17 | 18 | if( atom.inSpecMode() ) 19 | return 20 | 21 | require('atom-package-deps') 22 | .install('highlight-selected',true); 23 | } 24 | 25 | 26 | editorSubscriptions = new CompositeDisposable; 27 | selectionManager; 28 | configObserver; 29 | api; 30 | 31 | 32 | constructor ( selectionManager ){ 33 | 34 | this.selectionManager = selectionManager; 35 | 36 | if( showResultsOnScrollbar() ){ 37 | 38 | ScrollMarkersService.ensureScrollViewInstalled(); 39 | 40 | workspace 41 | .getTextEditors() 42 | .forEach((editor) => this.setScrollMarkerView(editor)); 43 | } 44 | 45 | this.editorSubscriptions 46 | .add( ... this.setupEditorSubscriptions() ); 47 | 48 | this.setupConfigObserver(); 49 | } 50 | 51 | 52 | destroy (){ 53 | this.configObserver.dispose(); 54 | this.editorSubscriptions.dispose(); 55 | } 56 | 57 | 58 | setScrollMarkerAPI ( api ){ 59 | this.api = api; 60 | } 61 | 62 | 63 | * setupEditorSubscriptions (){ 64 | 65 | yield workspace 66 | .observeTextEditors((editor) => 67 | this.setScrollMarkerView(editor)); 68 | 69 | yield workspace 70 | .onWillDestroyPaneItem(({ item }) => { 71 | 72 | if( item.constructor.name !== 'TextEditor' ) 73 | return 74 | 75 | this.destroyScrollMarkers(item); 76 | }); 77 | } 78 | 79 | 80 | setupConfigObserver (){ 81 | 82 | const onChange = ( enabled ) => { 83 | 84 | if( enabled ) 85 | ScrollMarkersService.ensureScrollViewInstalled(); 86 | 87 | const processMarkers = ( enabled ) 88 | ? ( editor ) => this.setScrollMarkerView(editor) 89 | : ( editor ) => this.destroyScrollMarkers(editor) ; 90 | 91 | workspace 92 | .getTextEditors() 93 | .forEach(processMarkers); 94 | } 95 | 96 | this.configObserver = config 97 | .observe('highlight-selected.showResultsOnScrollBar',onChange); 98 | } 99 | 100 | 101 | setScrollMarkerView ( editor ){ 102 | 103 | if( ! showResultsOnScrollbar() ) 104 | return 105 | 106 | 107 | const { selectionManager , api } = this; 108 | 109 | if( ! api ) 110 | return 111 | 112 | 113 | const view = api 114 | .scrollMarkerViewForEditor(editor); 115 | 116 | const { selectedMarkerLayer , visibleMarkerLayer } = 117 | selectionManager.editorToMarkerLayerMap[editor.id]; 118 | 119 | view 120 | .getLayer('highlight-selected-marker-layer') 121 | .syncToMarkerLayer(visibleMarkerLayer); 122 | 123 | view 124 | .getLayer('highlight-selected-selected-marker-layer') 125 | .syncToMarkerLayer(selectedMarkerLayer); 126 | } 127 | 128 | 129 | destroyScrollMarkers ( editor ){ 130 | 131 | const { api } = this; 132 | 133 | if( ! api ) 134 | return 135 | 136 | api 137 | .scrollMarkerViewForEditor(editor) 138 | .destroy(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Source/search-model.js: -------------------------------------------------------------------------------- 1 | 2 | const { getActiveEditor, getActiveEditors } = require('./utils/editor-finders'); 3 | const EarlyTerminationSignal = require('./errors/early-termination-signal'); 4 | const getNonWordCharacters = require('./utils/non-word-characters'); 5 | const isWordSelected = require('./utils/is-word-selected'); 6 | const escapeRegExp = require('./utils/escape-reg-exp'); 7 | 8 | 9 | const { config } = atom; 10 | 11 | 12 | module.exports = class SearchModel { 13 | 14 | static hideHighlightOnSelectedWord ( range , selections ){ 15 | 16 | if( ! config.get('highlight-selected.hideHighlightOnSelectedWord') ) 17 | return false; 18 | 19 | let outcome = false; 20 | 21 | for( let i = 0 ; i < selections.length ; i += 1 ){ 22 | 23 | const selection = selections[i]; 24 | const selectionRange = selection.getBufferRange(); 25 | 26 | outcome = 27 | range.start.column === selectionRange.start.column && 28 | range.start.row === selectionRange.start.row && 29 | range.end.column === selectionRange.end.column && 30 | range.end.row === selectionRange.end.row; 31 | 32 | if( outcome ) 33 | break; 34 | } 35 | 36 | return outcome 37 | } 38 | 39 | 40 | static makeClasses (){ 41 | 42 | let className = 'highlight-selected'; 43 | 44 | if( config.get('highlight-selected.lightTheme') ) 45 | className += ' light-theme'; 46 | 47 | if( config.get('highlight-selected.highlightBackground') ) 48 | className += ' background'; 49 | 50 | return className 51 | } 52 | 53 | 54 | // This functions replicates `\bTEXT\b` regex with allowed non-word characters to be something 55 | // like: `(^|[ @$#\.])TEXT([ @$#\.]|$)`. This allows unicode characters to be highlighted. Also, 56 | // it allows characters such as `@` in Ruby to selected and highlighted. 57 | // This is the first part of highlighting the whole words. We need to run another regex later to 58 | // ensure we only highlight the selection from the Atom Editor. 59 | 60 | static updateRegexSearchForWholeWords ( 61 | regexSearch , editor , lastSelection , 62 | regexFlags , text ){ 63 | 64 | if( ! config.get('highlight-selected.onlyHighlightWholeWords') ) 65 | return regexSearch 66 | 67 | if( ! isWordSelected(editor,lastSelection)) 68 | return regexSearch 69 | 70 | 71 | const selectionStart = lastSelection 72 | .getBufferRange().start; 73 | 74 | const nonWordCharacters = 75 | getNonWordCharacters(editor,selectionStart); 76 | 77 | const allowedCharactersToSelect = config 78 | .get('highlight-selected.allowedCharactersToSelect'); 79 | 80 | const nonWordCharactersToStrip = nonWordCharacters 81 | .replace(new RegExp(`[${allowedCharactersToSelect}]`,'g'),''); 82 | 83 | const regexForWholeWord = 84 | new RegExp(`[ \\t${escapeRegExp(nonWordCharactersToStrip)}]`,regexFlags); 85 | 86 | 87 | if( regexForWholeWord.test(text) ) 88 | return regexSearch 89 | 90 | return `(?:[ \\t${ 91 | escapeRegExp(nonWordCharacters) 92 | }]|^)(${ 93 | regexSearch 94 | })(?=[ \\t${ 95 | escapeRegExp(nonWordCharacters) 96 | }]|$)`; 97 | } 98 | 99 | 100 | constructor ( selectionManager ){ 101 | this.selectionManager = selectionManager; 102 | } 103 | 104 | 105 | handleSelection (){ 106 | 107 | const editor = getActiveEditor(); 108 | 109 | if( ! editor ) 110 | return 111 | 112 | this.selectionManager.removeAllMarkers(); 113 | 114 | if( this.selectionManager.disabled ) 115 | return; 116 | 117 | if( editor.getLastSelection().isEmpty() ) 118 | return; 119 | 120 | 121 | this.selections = editor 122 | .getSelections(); 123 | 124 | const lastSelection = editor 125 | .getLastSelection(); 126 | 127 | const text = lastSelection 128 | .getText(); 129 | 130 | 131 | if( text.length < config.get('highlight-selected.minimumLength') ) 132 | return 133 | 134 | if( text.includes('\n') ) 135 | return 136 | 137 | const regex = new RegExp('^\\s+$'); 138 | 139 | if( regex.test(text) ) 140 | return 141 | 142 | let regexFlags = 'g'; 143 | 144 | if( config.get('highlight-selected.ignoreCase') ) 145 | regexFlags += 'i'; 146 | 147 | const regexSearch = SearchModel 148 | .updateRegexSearchForWholeWords( 149 | escapeRegExp(text) , 150 | editor , 151 | lastSelection , 152 | regexFlags , 153 | text 154 | ); 155 | 156 | this.selectionManager.resultCount = 0; 157 | 158 | if( config.get('highlight-selected.highlightInPanes') ){ 159 | 160 | const originalEditor = editor; 161 | 162 | for ( const editor of getActiveEditors() ) 163 | this.highlightSelectionInEditor( editor , regexSearch , regexFlags , originalEditor ); 164 | 165 | } else { 166 | this.highlightSelectionInEditor(editor,regexSearch,regexFlags); 167 | } 168 | 169 | this.selectionManager.emitter.emit('did-finish-adding-markers'); 170 | } 171 | 172 | 173 | highlightSelectionInEditor ( editor , regexSearch , regexFlags , originalEditor ){ 174 | 175 | const maximumHighlights = atom.config 176 | .get('highlight-selected.maximumHighlights'); 177 | 178 | if( this.selectionManager.resultCount > maximumHighlights ) 179 | return 180 | 181 | const markerLayers = this.selectionManager 182 | .editorToMarkerLayerMap[editor.id]; 183 | 184 | if( ! markerLayers ) 185 | return 186 | 187 | const markerLayer = markerLayers.visibleMarkerLayer; 188 | const markerLayerForHiddenMarkers = markerLayers.selectedMarkerLayer; 189 | 190 | // We should have a marker layers. If not run away. 191 | 192 | if( ! markerLayer ) 193 | return 194 | 195 | if( ! markerLayerForHiddenMarkers ) 196 | return 197 | 198 | // HACK: `editor.scan` is a synchronous process which iterates the entire buffer, 199 | // executing a regex against every line and yielding each match. This can be 200 | // costly for very large files with many matches. 201 | // 202 | // While we can and do limit the maximum number of highlight markers, 203 | // `editor.scan` cannot be terminated early, meaning that we are forced to 204 | // pay the cost of iterating every line in the file, running the regex, and 205 | // returning matches, even if we shouldn't be creating any more markers. 206 | // 207 | // Instead, throw an exception. This isn't pretty, but it prevents the 208 | // scan from running to completion unnecessarily. 209 | 210 | try { 211 | 212 | editor.scan(new RegExp(regexSearch,regexFlags),(result) => { 213 | 214 | if( this.selectionManager.resultCount >= maximumHighlights ) 215 | throw new EarlyTerminationSignal 216 | 217 | let newResult = result; 218 | 219 | // The the following check allows the selection from the Atom Editor to have the marker on 220 | // it. If we do not redo the regex and update the found match, we will add a marker around 221 | // all non-word characters, rather than the allowed non-word characters. 222 | 223 | if( config.get('highlight-selected.onlyHighlightWholeWords') ) 224 | editor.scanInBufferRange(new RegExp(escapeRegExp(result.match[1])), result.range, (e) => newResult = e); 225 | 226 | if( ! newResult ) 227 | return 228 | 229 | this.selectionManager.resultCount += 1; 230 | 231 | const hideHighlight = SearchModel 232 | .hideHighlightOnSelectedWord( newResult.range , this.selections ); 233 | 234 | // If we want to hide the highlight on the selected word, we will add it to a different 235 | // marker layer. The hidden marker layer is used for by the `scroll-marker` API to show 236 | // matches. We do not tell the editor to decorate this marker layer. We also use fire 237 | // different events. This is so other packages and render them differently if they want. 238 | 239 | if( hideHighlight && originalEditor?.id === editor.id ){ 240 | 241 | const marker = markerLayerForHiddenMarkers.markBufferRange(newResult.range); 242 | 243 | this.selectionManager.emitter.emit('did-add-selected-marker', marker); 244 | this.selectionManager.emitter.emit('did-add-selected-marker-for-editor',{ marker , editor }); 245 | 246 | } else { 247 | 248 | const marker = markerLayer.markBufferRange(newResult.range); 249 | 250 | this.selectionManager.emitter.emit('did-add-marker', marker); 251 | this.selectionManager.emitter.emit('did-add-marker-for-editor',{ marker , editor }); 252 | } 253 | }); 254 | 255 | } catch ( error ){ 256 | 257 | if( error instanceof EarlyTerminationSignal ){ 258 | 259 | // If this is an early termination, just continue on. 260 | 261 | } else 262 | if( error.message === 'regular expression is too large' ){ 263 | 264 | // User has done a huge selection which exceed regex limit. 265 | // The user is most probably about to take action on the huge selection 266 | // by pressing ctrl-c or backspace … the user currently does not care about highlighting. 267 | // Therefore just continue on. 268 | 269 | } else { 270 | throw error; 271 | } 272 | } 273 | 274 | editor.decorateMarkerLayer(markerLayer,{ 275 | class : SearchModel.makeClasses() , 276 | type : 'highlight' 277 | }) 278 | } 279 | }; 280 | -------------------------------------------------------------------------------- /Source/selection-manager.js: -------------------------------------------------------------------------------- 1 | 2 | const { CompositeDisposable, Emitter } = require('atom'); 3 | const { getActiveEditor } = require('./utils/editor-finders'); 4 | const SearchModel = require('./search-model'); 5 | const debounce = require('debounce'); 6 | 7 | 8 | const { workspace , config } = atom; 9 | 10 | 11 | module.exports = class SelectionManager { 12 | 13 | editorToMarkerLayerMap = []; 14 | markerLayer = []; 15 | resultCount = 0; 16 | emitter = new Emitter; 17 | 18 | 19 | constructor (){ 20 | 21 | this.debouncedHandleSelection = this.debouncedHandleSelection.bind(this); 22 | 23 | this.searchModel = new SearchModel(this); 24 | 25 | this.editorSubscriptions = new CompositeDisposable; 26 | this.editorSubscriptions.add( 27 | workspace.observeTextEditors( 28 | (editor) => this.setupMarkerLayers(editor))); 29 | 30 | this.editorSubscriptions.add( 31 | workspace.onWillDestroyPaneItem((item) => { 32 | 33 | if( item.item.constructor.name !== 'TextEditor' ) 34 | return 35 | 36 | const editor = item.item; 37 | this.removeMarkers(editor.id); 38 | 39 | delete this.editorToMarkerLayerMap[editor.id]; 40 | }) 41 | ); 42 | 43 | this.enable(); 44 | this.listenForTimeoutChange(); 45 | 46 | this.activeItemSubscription = workspace 47 | .onDidChangeActivePaneItem(() => { 48 | this.debouncedHandleSelection(); 49 | return this.subscribeToActiveTextEditor(); 50 | }); 51 | 52 | this.subscribeToActiveTextEditor(); 53 | } 54 | 55 | 56 | destroy (){ 57 | 58 | this.handleSelectionDebounce.clear(); 59 | this.activeItemSubscription.dispose(); 60 | 61 | this.selectionSubscription?.dispose(); 62 | this.editorSubscriptions?.dispose(); 63 | } 64 | 65 | onDidAddMarker ( callback ){ 66 | const Grim = require('grim'); // eslint-disable-line global-require 67 | Grim.deprecate('Please do not use. This method will be removed.'); 68 | this.emitter.on('did-add-marker',callback); 69 | } 70 | 71 | onDidAddSelectedMarker ( callback ){ 72 | 73 | const Grim = require('grim'); 74 | Grim.deprecate('Please do not use. This method will be removed.'); 75 | this.emitter.on('did-add-selected-marker',callback); 76 | } 77 | 78 | onDidAddMarkerForEditor ( callback ){ 79 | this.emitter.on('did-add-marker-for-editor',callback); 80 | } 81 | 82 | onDidAddSelectedMarkerForEditor ( callback ){ 83 | this.emitter.on('did-add-selected-marker-for-editor',callback); 84 | } 85 | 86 | onDidFinishAddingMarkers ( callback ){ 87 | this.emitter.on('did-finish-adding-markers',callback); 88 | } 89 | 90 | onDidRemoveAllMarkers ( callback ){ 91 | this.emitter.on('did-remove-marker-layer',callback); 92 | } 93 | 94 | 95 | disable (){ 96 | this.disabled = true; 97 | return this.removeAllMarkers() 98 | } 99 | 100 | 101 | enable (){ 102 | this.disabled = false; 103 | return this.debouncedHandleSelection() 104 | } 105 | 106 | 107 | debouncedHandleSelection (){ 108 | 109 | if( ! this.handleSelectionDebounce ){ 110 | 111 | this.handleSelectionDebounce = debounce(() => { 112 | this.searchModel.handleSelection(); 113 | },config.get('highlight-selected.timeout')); 114 | } 115 | 116 | return this.handleSelectionDebounce() 117 | } 118 | 119 | 120 | listenForTimeoutChange (){ 121 | return config.onDidChange('highlight-selected.timeout', 122 | () => this.debouncedHandleSelection()); 123 | } 124 | 125 | 126 | subscribeToActiveTextEditor (){ 127 | 128 | this.selectionSubscription 129 | ?.dispose(); 130 | 131 | const editor = getActiveEditor(); 132 | 133 | if( ! editor ) 134 | return 135 | 136 | this.selectionSubscription = 137 | new CompositeDisposable; 138 | 139 | this.selectionSubscription.add( 140 | editor.onDidAddSelection( 141 | this.debouncedHandleSelection)); 142 | 143 | this.selectionSubscription.add( 144 | editor.onDidChangeSelectionRange( 145 | this.debouncedHandleSelection)); 146 | 147 | this.searchModel.handleSelection(); 148 | } 149 | 150 | 151 | removeAllMarkers (){ 152 | 153 | for ( const editorId in this.editorToMarkerLayerMap ) 154 | this.removeMarkers(editorId); 155 | } 156 | 157 | 158 | removeMarkers ( editorId ){ 159 | 160 | const layerMap = this.editorToMarkerLayerMap[ editorId ]; 161 | 162 | if( ! layerMap ) 163 | return; 164 | 165 | const { visibleMarkerLayer , selectedMarkerLayer } = layerMap; 166 | 167 | selectedMarkerLayer.clear(); 168 | visibleMarkerLayer.clear(); 169 | 170 | this.resultCount = 0; 171 | this.emitter.emit('did-remove-marker-layer'); 172 | } 173 | 174 | 175 | selectAll (){ 176 | 177 | const editor = getActiveEditor(); 178 | const markerLayers = this.editorToMarkerLayerMap[editor.id]; 179 | 180 | if( ! markerLayers ) 181 | return 182 | 183 | const ranges = [ 'visibleMarkerLayer' , 'selectedMarkerLayer' ] 184 | .map(( key ) => this.markerLayer[key]) 185 | .map(( layer ) => layer.getMarkers()) 186 | .flat() 187 | .map(( marker ) => marker.getBufferRange()); 188 | 189 | if( ranges.length > 0 ) 190 | editor.setSelectedBufferRanges(ranges,{ flash : true }); 191 | } 192 | 193 | 194 | setupMarkerLayers ( editor ){ 195 | 196 | const { id } = editor; 197 | 198 | // const layerMap = ( this.editorToMarkerLayerMap[ id ] ??= {} ); 199 | 200 | // layerMap.selectedMarkerLayer 201 | // ??= editor.addMarkerLayer(); 202 | 203 | // layerMap.visibleMarkerLayer 204 | // ??= editor.addMarkerLayer(); 205 | 206 | const layerMap = this.editorToMarkerLayerMap[ id ] ?? {}; 207 | 208 | if( ! layerMap.selectedMarkerLayer ) 209 | layerMap.selectedMarkerLayer = editor.addMarkerLayer(); 210 | 211 | if( ! layerMap.visibleMarkerLayer ) 212 | layerMap.visibleMarkerLayer = editor.addMarkerLayer(); 213 | 214 | this.editorToMarkerLayerMap[ id ] = layerMap; 215 | } 216 | }; 217 | -------------------------------------------------------------------------------- /Source/status-bar/status-bar-service.js: -------------------------------------------------------------------------------- 1 | 2 | const StatusBarView = require('./status-bar-view'); 3 | 4 | 5 | const { config } = atom; 6 | 7 | 8 | class StatusBarService { 9 | 10 | constructor ( statusBar , selectionManager ){ 11 | 12 | this.selectionManager = selectionManager; 13 | this.statusBar = statusBar; 14 | 15 | this.updateCount = this.updateCount.bind(this); 16 | 17 | this.listenForStatusBarConfigChange(); 18 | this.setupListeners(); 19 | this.setupStatusBarView(); 20 | } 21 | 22 | 23 | destroy (){ 24 | 25 | this.removeStatusBarView(); 26 | 27 | this.selectionSubscription?.dispose(); 28 | } 29 | 30 | 31 | listenForStatusBarConfigChange (){ 32 | return config.onDidChange('highlight-selected.showInStatusBar',( changed ) => { 33 | return ( changed.newValue ) 34 | ? this.setupStatusBarView() 35 | : this.removeStatusBarView() 36 | }); 37 | } 38 | 39 | 40 | setupListeners (){ 41 | 42 | const { selectionManager } = this; 43 | 44 | selectionManager.onDidFinishAddingMarkers(this.updateCount); 45 | selectionManager.onDidRemoveAllMarkers(this.updateCount); 46 | } 47 | 48 | 49 | setupStatusBarView (){ 50 | 51 | if( this.statusBarElement ) 52 | return 53 | 54 | if( ! config.get('highlight-selected.showInStatusBar') ) 55 | return 56 | 57 | this.statusBarElement = 58 | new StatusBarView; 59 | 60 | this.statusBarTile = this.statusBar 61 | .addLeftTile({ 62 | priority : 100 , 63 | item : this.statusBarElement.element 64 | }) 65 | } 66 | 67 | 68 | removeStatusBarView (){ 69 | 70 | if( ! this.statusBarElement) 71 | return 72 | 73 | this.statusBarElement.removeElement(); 74 | this.statusBarTile?.destroy(); 75 | 76 | this.statusBarElement = null; 77 | this.statusBarTile = null; 78 | } 79 | 80 | 81 | updateCount (){ 82 | 83 | const { statusBarElement , selectionManager } = this; 84 | 85 | statusBarElement?.updateCount( 86 | selectionManager.resultCount); 87 | } 88 | } 89 | 90 | 91 | 92 | module.exports = StatusBarService; 93 | -------------------------------------------------------------------------------- /Source/status-bar/status-bar-view.js: -------------------------------------------------------------------------------- 1 | 2 | const { config } = atom; 3 | 4 | 5 | module.exports = class StatusBarView { 6 | 7 | constructor (){ 8 | 9 | this.removeElement = this.removeElement.bind(this); 10 | 11 | this.element = document.createElement('div'); 12 | 13 | this.element.classList 14 | .add('highlight-selected-status','inline-block'); 15 | } 16 | 17 | updateCount ( count ){ 18 | 19 | const statusBarString = config 20 | .get('highlight-selected.statusBarString'); 21 | 22 | this.element.textContent = 23 | statusBarString.replace('%c',count); 24 | 25 | const { classList } = this.element; 26 | 27 | if( count > 0 ) 28 | classList.remove('highlight-selected-hidden'); 29 | else 30 | classList.add('highlight-selected-hidden'); 31 | } 32 | 33 | removeElement (){ 34 | 35 | this.element.parentNode 36 | .removeChild(this.element); 37 | 38 | this.element = null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Source/utils/editor-finders.js: -------------------------------------------------------------------------------- 1 | 2 | const { workspace } = atom; 3 | 4 | 5 | function getActiveEditor (){ 6 | return workspace.getActiveTextEditor(); 7 | } 8 | 9 | 10 | const isTextEditor = ( item ) => 11 | item?.constructor.name === 'TextEditor'; 12 | 13 | 14 | function getActiveEditors (){ 15 | return workspace 16 | .getPanes() 17 | .map(({ activeItem }) => activeItem) 18 | .filter(isTextEditor) 19 | } 20 | 21 | 22 | 23 | module.exports = { getActiveEditor , getActiveEditors } 24 | -------------------------------------------------------------------------------- /Source/utils/escape-reg-exp.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const unescaped = /[-/\\^$*+?.()|[\]{}]/g; 4 | 5 | const escaped = '\\$&'; 6 | 7 | 8 | function escapeRegExp ( string = '' ){ 9 | return string 10 | .replace(unescaped,escaped) 11 | } 12 | 13 | 14 | module.exports = escapeRegExp; 15 | -------------------------------------------------------------------------------- /Source/utils/is-word-selected.js: -------------------------------------------------------------------------------- 1 | 2 | const getNonWordCharacters = require('./non-word-characters'); 3 | const escapeRegExp = require('./escape-reg-exp'); 4 | const { Range } = require('atom'); 5 | 6 | 7 | function isNonWord ( editor , range ){ 8 | 9 | const { start } = range; 10 | 11 | const nonWords = escapeRegExp( 12 | getNonWordCharacters(editor,start)); 13 | 14 | const pattern = 15 | new RegExp(`[ \t${ nonWords }]`); 16 | 17 | const text = editor 18 | .getTextInBufferRange(range); 19 | 20 | return pattern 21 | .test(text) 22 | } 23 | 24 | 25 | function isNonWordCharacterToTheLeft ( editor , selection ){ 26 | 27 | const { start } = selection 28 | .getBufferRange(); 29 | 30 | const range = Range 31 | .fromPointWithDelta(start,0,-1); 32 | 33 | return isNonWord(editor,range) 34 | } 35 | 36 | 37 | function isNonWordCharacterToTheRight ( editor , selection ){ 38 | 39 | const { end } = selection 40 | .getBufferRange(); 41 | 42 | const range = Range 43 | .fromPointWithDelta(end,0,1); 44 | 45 | return isNonWord(editor,range); 46 | } 47 | 48 | 49 | function isWordSelected ( editor , selection ){ 50 | 51 | const range = selection 52 | .getBufferRange(); 53 | 54 | if( ! range.isSingleLine() ) 55 | return false; 56 | 57 | 58 | const { start , end } = range; 59 | 60 | const lineRange = editor 61 | .bufferRangeForBufferRow(start.row); 62 | 63 | const nonWordCharacterToTheLeft = 64 | start.isEqual(lineRange.start) || 65 | isNonWordCharacterToTheLeft(editor,selection); 66 | 67 | const nonWordCharacterToTheRight = 68 | end.isEqual(lineRange.end) || 69 | isNonWordCharacterToTheRight(editor,selection); 70 | 71 | return nonWordCharacterToTheRight 72 | && nonWordCharacterToTheLeft 73 | } 74 | 75 | 76 | module.exports = isWordSelected; 77 | -------------------------------------------------------------------------------- /Source/utils/non-word-characters.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const { config } = atom; 4 | 5 | 6 | function getNonWordCharacters ( editor , point ){ 7 | 8 | const scope = editor 9 | .scopeDescriptorForBufferPosition(point); 10 | 11 | const nonWordCharacters = config 12 | .get('editor.nonWordCharacters',{ scope }); 13 | 14 | return nonWordCharacters; 15 | } 16 | 17 | 18 | module.exports = getNonWordCharacters; 19 | -------------------------------------------------------------------------------- /Tests/fixtures/hex.md: -------------------------------------------------------------------------------- 1 | 00000000 00000000 00000000 2 | 00000000.00000000.00000000 3 | 00000000-00000000-00000000 4 | 5 | 00000000 00000000 00000000 6 | 00000000..00000000..00000000 7 | 00000000--00000000--00000000 8 | -------------------------------------------------------------------------------- /Tests/fixtures/sample.coffee: -------------------------------------------------------------------------------- 1 | # Sample file for ensuring the regions get added. 2 | # Output Fizz on multiples of 3, Output Buzz on multiples of 5 3 | # Output FizzBuzz on multiples of 3 and 5 4 | 5 | output = "" 6 | 7 | i = 1 8 | while i <= 100 9 | string = "#{i} " 10 | string += 'Fizz' if i % 3 is 0 11 | string += 'Buzz' if i % 5 is 0 12 | output += "#{string}\n" 13 | i++ 14 | -------------------------------------------------------------------------------- /Tests/fixtures/sample.php: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /Tests/highlight-selected-spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { Range, Point } = require('atom'); 3 | 4 | // This spec is more of an end-to-end test. 5 | describe('HighlightSelected', () => { 6 | let [ 7 | workspaceElement, 8 | minimap, 9 | editor, 10 | editorElement, 11 | highlightSelected, 12 | minimapHS, 13 | minimapModule, 14 | ] = Array.from([]); 15 | 16 | const hasMinimap = 17 | atom.packages.getAvailablePackageNames().indexOf('minimap') !== -1 && 18 | atom.packages.getAvailablePackageNames().indexOf('minimap-highlight-selected') !== -1; 19 | 20 | beforeEach(() => { 21 | workspaceElement = atom.views.getView(atom.workspace); 22 | atom.project.setPaths([path.join(__dirname, 'fixtures')]); 23 | }); 24 | 25 | afterEach(() => { 26 | highlightSelected.deactivate(); 27 | if (minimapHS) { 28 | minimapHS.deactivate(); 29 | } 30 | if (minimapModule) { 31 | minimapModule.deactivate(); 32 | } 33 | }); 34 | 35 | describe('when opening a coffee file', () => { 36 | beforeEach(() => { 37 | waitsForPromise(() => 38 | atom.packages.activatePackage('status-bar').then(() => { 39 | workspaceElement.querySelector('status-bar'); 40 | }) 41 | ); 42 | 43 | waitsForPromise(() => 44 | atom.packages.activatePackage('highlight-selected').then(({ mainModule }) => { 45 | highlightSelected = mainModule; 46 | }) 47 | ); 48 | 49 | if (hasMinimap) { 50 | waitsForPromise(() => 51 | atom.packages.activatePackage('minimap').then(({ mainModule }) => { 52 | minimapModule = mainModule; 53 | }) 54 | ); 55 | waitsForPromise(() => 56 | atom.packages.activatePackage('minimap-highlight-selected').then(({ mainModule }) => { 57 | minimapHS = mainModule; 58 | }) 59 | ); 60 | } 61 | 62 | waitsForPromise(() => 63 | atom.workspace.open('sample.coffee').then( 64 | () => null, 65 | (error) => { 66 | throw error.stack; 67 | } 68 | ) 69 | ); 70 | 71 | runs(() => { 72 | jasmine.attachToDOM(workspaceElement); 73 | editor = atom.workspace.getActiveTextEditor(); 74 | editorElement = atom.views.getView(editor); 75 | editorElement.setHeight(250); 76 | editorElement.component.measureDimensions(); 77 | }); 78 | }); 79 | 80 | describe('updates debounce when config is changed', () => { 81 | beforeEach(() => { 82 | spyOn(highlightSelected.selectionManager, 'debouncedHandleSelection'); 83 | atom.config.set('highlight-selected.timeout', 20000); 84 | }); 85 | 86 | it('calls createDebouce', () => { 87 | expect(highlightSelected.selectionManager.debouncedHandleSelection).toHaveBeenCalled(); 88 | }); 89 | }); 90 | 91 | describe('when a whole word is selected', () => { 92 | beforeEach(() => { 93 | const range = new Range(new Point(8, 2), new Point(8, 8)); 94 | editor.setSelectedBufferRange(range); 95 | advanceClock(20000); 96 | }); 97 | 98 | it('adds the decoration to all words', () => { 99 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(4); 100 | }); 101 | 102 | it('creates the highlight selected status bar element', () => { 103 | expect(workspaceElement.querySelector('status-bar')).toExist(); 104 | expect(workspaceElement.querySelector('.highlight-selected-status')).toExist(); 105 | }); 106 | 107 | it('updates the status bar with highlights number', () => { 108 | const content = workspaceElement.querySelector('.highlight-selected-status').innerHTML; 109 | expect(content).toBe('Highlighted: 4'); 110 | }); 111 | 112 | describe('when the status bar is disabled', () => { 113 | beforeEach(() => atom.config.set('highlight-selected.showInStatusBar', false)); 114 | 115 | it("highlight isn't attached", () => { 116 | expect(workspaceElement.querySelector('status-bar')).toExist(); 117 | expect(workspaceElement.querySelector('.highlight-selected-status')).not.toExist(); 118 | }); 119 | }); 120 | }); 121 | 122 | describe('when hide highlight on selected word is enabled', () => { 123 | beforeEach(() => atom.config.set('highlight-selected.hideHighlightOnSelectedWord', true)); 124 | 125 | describe('when a single line is selected', () => { 126 | beforeEach(() => { 127 | const range = new Range(new Point(8, 2), new Point(8, 8)); 128 | editor.setSelectedBufferRange(range); 129 | advanceClock(20000); 130 | }); 131 | 132 | it('adds the decoration only on selected words', () => { 133 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(3); 134 | }); 135 | }); 136 | 137 | describe('when multi lines are selected', () => { 138 | beforeEach(() => { 139 | const range1 = new Range(new Point(8, 2), new Point(8, 8)); 140 | const range2 = new Range(new Point(9, 2), new Point(9, 8)); 141 | editor.setSelectedBufferRanges([range1, range2]); 142 | advanceClock(20000); 143 | }); 144 | 145 | it('adds the decoration only on selected words', () => { 146 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(2); 147 | }); 148 | }); 149 | }); 150 | 151 | describe("leading whitespace doesn't get used", () => { 152 | beforeEach(() => { 153 | const range = new Range(new Point(8, 0), new Point(8, 8)); 154 | editor.setSelectedBufferRange(range); 155 | advanceClock(20000); 156 | }); 157 | 158 | it("doesn't add regions", () => { 159 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0); 160 | }); 161 | }); 162 | 163 | describe('ignores whitespace only selections', () => { 164 | beforeEach(() => atom.config.set('highlight-selected.onlyHighlightWholeWords', false)); 165 | 166 | it('ignores space only selections', () => { 167 | const range = new Range(new Point(8, 0), new Point(8, 2)); 168 | editor.setSelectedBufferRange(range); 169 | advanceClock(20000); 170 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0); 171 | }); 172 | 173 | it('allows selections to include whitespace', () => { 174 | const range = new Range(new Point(8, 0), new Point(8, 8)); 175 | editor.setSelectedBufferRange(range); 176 | advanceClock(20000); 177 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(3); 178 | }); 179 | }); 180 | 181 | describe('ignores selections that contain a new line', () => { 182 | beforeEach(() => atom.config.set('highlight-selected.onlyHighlightWholeWords', false)); 183 | 184 | it('ignores a selection of a single newline', () => { 185 | const range = new Range(new Point(7, 14), new Point(8, 0)); // '\n' 186 | editor.setSelectedBufferRange(range); 187 | advanceClock(20000); 188 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0); 189 | }); 190 | 191 | it('ignores any selection containing a newline', () => { 192 | const range = new Range(new Point(7, 14), new Point(8, 8)); // '\n string' 193 | editor.setSelectedBufferRange(range); 194 | advanceClock(20000); 195 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0); 196 | }); 197 | }); 198 | 199 | describe('will highlight non whole words', () => { 200 | beforeEach(() => { 201 | atom.config.set('highlight-selected.onlyHighlightWholeWords', false); 202 | const range = new Range(new Point(10, 13), new Point(10, 17)); 203 | editor.setSelectedBufferRange(range); 204 | advanceClock(20000); 205 | }); 206 | 207 | it('does add regions', () => { 208 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(3); 209 | }); 210 | }); 211 | 212 | describe('will not highlight non whole words', () => { 213 | beforeEach(() => { 214 | atom.config.set('highlight-selected.onlyHighlightWholeWords', true); 215 | const range = new Range(new Point(10, 13), new Point(10, 17)); 216 | editor.setSelectedBufferRange(range); 217 | advanceClock(20000); 218 | }); 219 | 220 | it('does add regions', () => { 221 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(2); 222 | }); 223 | }); 224 | 225 | describe('will not highlight less than minimum length', () => { 226 | beforeEach(() => { 227 | atom.config.set('highlight-selected.minimumLength', 7); 228 | const range = new Range(new Point(4, 0), new Point(4, 6)); 229 | editor.setSelectedBufferRange(range); 230 | advanceClock(20000); 231 | }); 232 | 233 | it("doesn't add regions", () => 234 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0)); 235 | }); 236 | 237 | describe('will not highlight words in different case', () => { 238 | beforeEach(() => { 239 | const range = new Range(new Point(4, 0), new Point(4, 6)); 240 | editor.setSelectedBufferRange(range); 241 | advanceClock(20000); 242 | }); 243 | 244 | it('does add regions', () => { 245 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(2); 246 | }); 247 | }); 248 | 249 | describe('will highlight words in different case', () => { 250 | beforeEach(() => { 251 | atom.config.set('highlight-selected.ignoreCase', true); 252 | const range = new Range(new Point(4, 0), new Point(4, 6)); 253 | editor.setSelectedBufferRange(range); 254 | advanceClock(20000); 255 | }); 256 | 257 | it('does add regions', () => { 258 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(5); 259 | }); 260 | 261 | describe('adds background to selected', () => { 262 | beforeEach(() => { 263 | atom.config.set('highlight-selected.highlightBackground', true); 264 | const range = new Range(new Point(8, 2), new Point(8, 8)); 265 | editor.setSelectedBufferRange(range); 266 | advanceClock(20000); 267 | }); 268 | 269 | it('adds the background to all highlights', () => { 270 | expect( 271 | editorElement.querySelectorAll('.highlight-selected.background .region') 272 | ).toHaveLength(4); 273 | }); 274 | }); 275 | 276 | describe('adds light theme to selected', () => { 277 | beforeEach(() => { 278 | atom.config.set('highlight-selected.lightTheme', true); 279 | const range = new Range(new Point(8, 2), new Point(8, 8)); 280 | editor.setSelectedBufferRange(range); 281 | advanceClock(20000); 282 | }); 283 | 284 | it('adds the background to all highlights', () => 285 | expect( 286 | editorElement.querySelectorAll('.highlight-selected.light-theme .region') 287 | ).toHaveLength(4)); 288 | }); 289 | }); 290 | 291 | if (hasMinimap) { 292 | describe('minimap highlight selected still works', () => { 293 | beforeEach(() => { 294 | editor = atom.workspace.getActiveTextEditor(); 295 | minimap = minimapModule.minimapForEditor(editor); 296 | 297 | spyOn(minimap, 'decorateMarker').andCallThrough(); 298 | const range = new Range(new Point(8, 2), new Point(8, 8)); 299 | editor.setSelectedBufferRange(range); 300 | advanceClock(20000); 301 | }); 302 | 303 | it('adds a decoration for the selection in the minimap', () => { 304 | expect(minimap.decorateMarker).toHaveBeenCalled(); 305 | }); 306 | }); 307 | } 308 | }); 309 | 310 | describe('when opening a php file', () => { 311 | beforeEach(() => { 312 | waitsForPromise(() => 313 | atom.packages.activatePackage('highlight-selected').then(({ mainModule }) => { 314 | highlightSelected = mainModule; 315 | }) 316 | ); 317 | 318 | waitsForPromise(() => 319 | atom.workspace.open('sample.php').then( 320 | () => editor, 321 | (error) => { 322 | throw error.stack; 323 | } 324 | ) 325 | ); 326 | 327 | waitsForPromise(() => atom.packages.activatePackage('language-php')); 328 | 329 | runs(() => { 330 | jasmine.attachToDOM(workspaceElement); 331 | editor = atom.workspace.getActiveTextEditor(); 332 | editorElement = atom.views.getView(editor); 333 | }); 334 | }); 335 | 336 | describe("being able to highlight variables with '$'", () => { 337 | beforeEach(() => { 338 | atom.config.set('highlight-selected.onlyHighlightWholeWords', true); 339 | const range = new Range(new Point(1, 2), new Point(1, 7)); 340 | editor.setSelectedBufferRange(range); 341 | advanceClock(20000); 342 | }); 343 | 344 | it('finds 3 regions', () => { 345 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(3); 346 | }); 347 | }); 348 | 349 | // describe("not being able to highlight variables when not selecting '$'", () => { 350 | // beforeEach(() => { 351 | // atom.config.set('highlight-selected.onlyHighlightWholeWords', true); 352 | // const range = new Range(new Point(1, 3), new Point(1, 7)); 353 | // editor.setSelectedBufferRange(range); 354 | // advanceClock(20000); 355 | // }); 356 | 357 | // it('finds 0 regions', () => { 358 | // expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(0); 359 | // }); 360 | // }); 361 | 362 | // describe("being able to highlight other strings when not selecting '@'", () => { 363 | // beforeEach(() => { 364 | // atom.config.set('highlight-selected.onlyHighlightWholeWords', true); 365 | // const range = new Range(new Point(3, 6), new Point(3, 10)); 366 | // editor.setSelectedBufferRange(range); 367 | // advanceClock(20000); 368 | // }); 369 | 370 | // it('finds 2 regions', () => { 371 | // expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(2); 372 | // }); 373 | // }); 374 | }); 375 | 376 | describe('when opening a file with hex like data', () => { 377 | beforeEach(() => { 378 | waitsForPromise(() => 379 | atom.packages.activatePackage('status-bar').then(() => { 380 | workspaceElement.querySelector('status-bar'); 381 | }) 382 | ); 383 | 384 | waitsForPromise(() => 385 | atom.packages.activatePackage('highlight-selected').then(({ mainModule }) => { 386 | highlightSelected = mainModule; 387 | }) 388 | ); 389 | 390 | if (hasMinimap) { 391 | waitsForPromise(() => 392 | atom.packages.activatePackage('minimap').then(({ mainModule }) => { 393 | minimapModule = mainModule; 394 | }) 395 | ); 396 | waitsForPromise(() => 397 | atom.packages.activatePackage('minimap-highlight-selected').then(({ mainModule }) => { 398 | minimapHS = mainModule; 399 | }) 400 | ); 401 | } 402 | 403 | waitsForPromise(() => 404 | atom.workspace.open('hex.md').then( 405 | () => null, 406 | (error) => { 407 | throw error.stack; 408 | } 409 | ) 410 | ); 411 | 412 | runs(() => { 413 | jasmine.attachToDOM(workspaceElement); 414 | editor = atom.workspace.getActiveTextEditor(); 415 | editorElement = atom.views.getView(editor); 416 | editorElement.setHeight(250); 417 | editorElement.component.measureDimensions(); 418 | }); 419 | }); 420 | 421 | describe("being able to highlight the first '00000000'", () => { 422 | beforeEach(() => { 423 | const range = new Range(new Point(0, 0), new Point(0, 8)); 424 | editor.setSelectedBufferRange(range); 425 | advanceClock(20000); 426 | }); 427 | 428 | it('finds 18 regions', () => { 429 | expect(editorElement.querySelectorAll('.highlight-selected .region')).toHaveLength(18); 430 | }); 431 | }); 432 | }); 433 | 434 | describe('when opening a big file', () => { 435 | beforeEach(() => { 436 | waitsForPromise(() => 437 | atom.packages.activatePackage('status-bar').then(() => { 438 | workspaceElement.querySelector('status-bar'); 439 | }) 440 | ); 441 | 442 | waitsForPromise(() => 443 | atom.packages.activatePackage('highlight-selected').then(({ mainModule }) => { 444 | highlightSelected = mainModule; 445 | }) 446 | ); 447 | 448 | if (hasMinimap) { 449 | waitsForPromise(() => 450 | atom.packages.activatePackage('minimap').then(({ mainModule }) => { 451 | minimapModule = mainModule; 452 | }) 453 | ); 454 | waitsForPromise(() => 455 | atom.packages.activatePackage('minimap-highlight-selected').then(({ mainModule }) => { 456 | minimapHS = mainModule; 457 | }) 458 | ); 459 | } 460 | 461 | waitsForPromise(() => 462 | atom.workspace.open().then( 463 | () => null, 464 | (error) => { 465 | throw error.stack; 466 | } 467 | ) 468 | ); 469 | 470 | runs(() => { 471 | jasmine.attachToDOM(workspaceElement); 472 | editor = atom.workspace.getActiveTextEditor(); 473 | editor.setText('a'.repeat(40000)); 474 | editorElement = atom.views.getView(editor); 475 | editorElement.setHeight(250); 476 | editorElement.component.measureDimensions(); 477 | }); 478 | }); 479 | 480 | describe('when doing a big selection', () => { 481 | // see: https://github.com/richrace/highlight-selected/issues/206 482 | beforeEach(() => { 483 | const range = new Range(new Point(0, 3), new Point(0, 38000)); 484 | editor.setSelectedBufferRange(range); 485 | advanceClock(20000); 486 | }); 487 | 488 | it('updates the status bar with highlights number', () => { 489 | const content = workspaceElement.querySelector('.highlight-selected-status').innerHTML; 490 | expect(content).toBe('Highlighted: 0'); 491 | }); 492 | 493 | describe('when the status bar is disabled', () => { 494 | beforeEach(() => atom.config.set('highlight-selected.showInStatusBar', false)); 495 | 496 | it("highlight isn't attached", () => { 497 | expect(workspaceElement.querySelector('status-bar')).toExist(); 498 | expect(workspaceElement.querySelector('.highlight-selected-status')).not.toExist(); 499 | }); 500 | }); 501 | }); 502 | }); 503 | }); 504 | -------------------------------------------------------------------------------- /Tests/main-spec.js: -------------------------------------------------------------------------------- 1 | const { CompositeDisposable } = require('atom'); 2 | const main = require('../Source/App.js'); 3 | const SelectionManager = require('../Source/selection-manager.js'); 4 | const StatusBarService = require('../Source/status-bar/status-bar-service.js'); 5 | const ScrollMarkersService = require('../Source/scroll-markers/scroll-markers-service.js'); 6 | 7 | describe('Main', () => { 8 | 9 | describe('active', () => { 10 | it('creates a SelectionManager', () => { 11 | main.activate(); 12 | expect(main.selectionManager instanceof SelectionManager).toBe(true); 13 | }); 14 | 15 | it('will listen to commands', () => { 16 | spyOn(main, 'listenForCommands').andCallThrough(); 17 | main.activate(); 18 | expect(main.listenForCommands).toHaveBeenCalled(); 19 | }); 20 | 21 | it('has subscriptions that is a CompositeDisposable', () => { 22 | main.activate(); 23 | expect(main.subscriptions instanceof CompositeDisposable).toBe(true); 24 | }); 25 | }); 26 | 27 | describe('deactivate', () => { 28 | it('destroys the selection manager and sets it to null', () => { 29 | const selectionManager = jasmine.createSpyObj('SelectionManager', ['destroy']); 30 | main.selectionManager = selectionManager; 31 | main.deactivate(); 32 | expect(selectionManager.destroy).toHaveBeenCalled(); 33 | expect(main.selectionManager).toBeNull(); 34 | }); 35 | 36 | it('disposes of the subscriptions and sets it to null', () => { 37 | const subscriptions = jasmine.createSpyObj('subscriptions', ['dispose']); 38 | main.subscriptions = subscriptions; 39 | main.deactivate(); 40 | expect(subscriptions.dispose).toHaveBeenCalled(); 41 | expect(main.subscriptions).toBeNull(); 42 | }); 43 | 44 | it('destroys the scrollMarkersService and sets it to null', () => { 45 | const scrollMarkersService = jasmine.createSpyObj('scrollMarkersService', ['destroy']); 46 | main.scrollMarkersService = scrollMarkersService; 47 | main.deactivate(); 48 | expect(scrollMarkersService.destroy).toHaveBeenCalled(); 49 | expect(main.scrollMarkersService).toBeNull(); 50 | }); 51 | 52 | it('destroys the statusBarService and sets it to null', () => { 53 | const statusBarService = jasmine.createSpyObj('statusBarService', ['destroy']); 54 | main.statusBarService = statusBarService; 55 | main.deactivate(); 56 | expect(statusBarService.destroy).toHaveBeenCalled(); 57 | expect(main.statusBarService).toBeNull(); 58 | }); 59 | }); 60 | 61 | describe('consumeStatusBar', () => { 62 | let statusBar; 63 | let selectionManager; 64 | 65 | beforeEach(() => { 66 | statusBar = {}; 67 | selectionManager = { 68 | onDidFinishAddingMarkers: () => {}, 69 | onDidRemoveAllMarkers: () => {}, 70 | }; 71 | main.selectionManager = selectionManager; 72 | main.consumeStatusBar(statusBar); 73 | }); 74 | 75 | it('creates a StatusBarService', () => { 76 | expect(main.statusBarService instanceof StatusBarService).toBe(true); 77 | }); 78 | 79 | it('assings selection manager correctly', () => { 80 | expect(main.statusBarService.selectionManager).toBe(selectionManager); 81 | }); 82 | 83 | it('assings status bar object correctly', () => { 84 | expect(main.statusBarService.statusBar).toBe(statusBar); 85 | }); 86 | }); 87 | 88 | describe('toggle', () => { 89 | let selectionManager; 90 | 91 | describe('when selection manager is disabled', () => { 92 | beforeEach(() => { 93 | selectionManager = { 94 | disabled: true, 95 | enable: () => {}, 96 | disable: () => {}, 97 | }; 98 | 99 | spyOn(selectionManager, 'enable'); 100 | spyOn(selectionManager, 'disable'); 101 | main.selectionManager = selectionManager; 102 | }); 103 | 104 | it('enables the selection manager', () => { 105 | main.toggle(); 106 | expect(selectionManager.enable).toHaveBeenCalled(); 107 | expect(selectionManager.disable).not.toHaveBeenCalled(); 108 | }); 109 | }); 110 | 111 | describe('when selection manager is enabled', () => { 112 | beforeEach(() => { 113 | selectionManager = { 114 | disabled: false, 115 | enable: () => {}, 116 | disable: () => {}, 117 | }; 118 | 119 | spyOn(selectionManager, 'enable'); 120 | spyOn(selectionManager, 'disable'); 121 | main.selectionManager = selectionManager; 122 | }); 123 | 124 | it('disables the selection manager', () => { 125 | main.toggle(); 126 | expect(selectionManager.enable).not.toHaveBeenCalled(); 127 | expect(selectionManager.disable).toHaveBeenCalled(); 128 | }); 129 | }); 130 | }); 131 | 132 | describe('selectAll', () => { 133 | it('calls selectAll on selectionMananger', () => { 134 | const selectionManager = jasmine.createSpyObj('selectionManager', ['selectAll']); 135 | main.selectionManager = selectionManager; 136 | main.selectAll(); 137 | expect(selectionManager.selectAll).toHaveBeenCalled(); 138 | }); 139 | }); 140 | 141 | describe('consumeScrollMarker', () => { 142 | let scrollMarkerAPI; 143 | let selectionManager; 144 | 145 | beforeEach(() => { 146 | scrollMarkerAPI = {}; 147 | selectionManager = { 148 | onDidFinishAddingMarkers: () => {}, 149 | onDidRemoveAllMarkers: () => {}, 150 | }; 151 | main.scrollMarkersService = new ScrollMarkersService(selectionManager); 152 | main.selectionManager = selectionManager; 153 | main.consumeScrollMarker(scrollMarkerAPI); 154 | }); 155 | 156 | it('creates a ScrollMarkersService', () => { 157 | expect(main.scrollMarkersService instanceof ScrollMarkersService).toBe(true); 158 | }); 159 | 160 | it('assings selection manager correctly', () => { 161 | expect(main.scrollMarkersService.selectionManager).toBe(selectionManager); 162 | }); 163 | 164 | it('assings status bar object correctly', () => { 165 | expect(main.scrollMarkersService.api).toBe(scrollMarkerAPI); 166 | }); 167 | }); 168 | 169 | describe('listenForCommands', () => { 170 | beforeEach(() => { 171 | main.listenForCommands(); 172 | }); 173 | 174 | it('adds a toggle command', () => { 175 | expect(atom.commands.registeredCommands['highlight-selected:toggle']).toBe(true); 176 | }); 177 | 178 | it('and select all commands', () => { 179 | expect(atom.commands.registeredCommands['highlight-selected:select-all']).toBe(true); 180 | }); 181 | }); 182 | }); 183 | -------------------------------------------------------------------------------- /Tests/scroll-markers/scroll-markers-service-spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const ScrollMarkersService = require('../../Source/scroll-markers/scroll-markers-service'); 3 | 4 | describe('ScrollMarkersService', () => { 5 | let scrollMarkersService; 6 | let selectionManager; 7 | let scrollMarkerApi; 8 | 9 | const createScrollMarkersService = () => { 10 | selectionManager = { 11 | onDidFinishAddingMarkers: () => {}, 12 | onDidRemoveAllMarkers: () => {}, 13 | }; 14 | spyOn(selectionManager, 'onDidFinishAddingMarkers'); 15 | spyOn(selectionManager, 'onDidRemoveAllMarkers'); 16 | scrollMarkerApi = {}; 17 | scrollMarkersService = new ScrollMarkersService(selectionManager); 18 | scrollMarkersService.setScrollMarkerAPI(scrollMarkerApi); 19 | }; 20 | 21 | beforeEach(() => { 22 | waitsForPromise(() => atom.packages.activatePackage('highlight-selected')); 23 | atom.config.set('highlight-selected.showResultsOnScrollBar', true); 24 | }); 25 | 26 | describe('constructor', () => { 27 | describe('when show results on scroll bar is enabled', () => { 28 | beforeEach(() => { 29 | atom.config.set('highlight-selected.showResultsOnScrollBar', true); 30 | }); 31 | 32 | it('ensures we have scroll marker package installed', () => { 33 | spyOn(ScrollMarkersService, 'ensureScrollViewInstalled'); 34 | createScrollMarkersService(); 35 | expect(ScrollMarkersService.ensureScrollViewInstalled).toHaveBeenCalled(); 36 | }); 37 | }); 38 | }); 39 | 40 | describe('destroyScrollMarkers', () => { 41 | beforeEach(() => { 42 | createScrollMarkersService(); 43 | }); 44 | 45 | describe('when there is no scroll marker API', () => { 46 | it('does not blow up', () => { 47 | scrollMarkersService.api = null; 48 | scrollMarkersService.destroyScrollMarkers(); 49 | }); 50 | }); 51 | 52 | it('destroys the marker view from the scroll bar API', () => { 53 | const editor = {}; 54 | const markerView = { destroy: () => {} }; 55 | scrollMarkerApi.scrollMarkerViewForEditor = () => markerView; 56 | spyOn(scrollMarkerApi, 'scrollMarkerViewForEditor').andCallThrough(); 57 | spyOn(markerView, 'destroy'); 58 | 59 | scrollMarkersService.destroyScrollMarkers(editor); 60 | expect(scrollMarkerApi.scrollMarkerViewForEditor).toHaveBeenCalledWith(editor); 61 | expect(markerView.destroy).toHaveBeenCalled(); 62 | }); 63 | }); 64 | 65 | describe('setScrollMarkerView', () => { 66 | const editor = { id: 9999 }; 67 | const layer = { syncToMarkerLayer: () => {} }; 68 | const markerView = { getLayer: () => layer }; 69 | const visibleMarkerLayer = {}; 70 | const selectedMarkerLayer = {}; 71 | 72 | beforeEach(() => { 73 | createScrollMarkersService(); 74 | scrollMarkerApi.scrollMarkerViewForEditor = () => markerView; 75 | selectionManager.editorToMarkerLayerMap = {}; 76 | selectionManager.editorToMarkerLayerMap[editor.id] = { 77 | visibleMarkerLayer, 78 | selectedMarkerLayer, 79 | }; 80 | }); 81 | 82 | describe('when show results on scroll bar is disabled', () => { 83 | beforeEach(() => { 84 | atom.config.set('highlight-selected.showResultsOnScrollBar', false); 85 | }); 86 | 87 | it('does not use the api', () => { 88 | scrollMarkerApi.scrollMarkerViewForEditor = () => {}; 89 | spyOn(scrollMarkerApi, 'scrollMarkerViewForEditor'); 90 | scrollMarkersService.setScrollMarkerView(); 91 | expect(scrollMarkerApi.scrollMarkerViewForEditor).not.toHaveBeenCalled(); 92 | }); 93 | }); 94 | 95 | describe('when the scroll marker API is not set', () => { 96 | it('does not blow up', () => { 97 | scrollMarkersService.api = null; 98 | expect(scrollMarkersService.setScrollMarkerView()).toBeUndefined(); 99 | }); 100 | }); 101 | 102 | it('gets the scroll marker view for the editor', () => { 103 | spyOn(scrollMarkerApi, 'scrollMarkerViewForEditor').andCallThrough(); 104 | 105 | scrollMarkersService.setScrollMarkerView(editor); 106 | expect(scrollMarkerApi.scrollMarkerViewForEditor).toHaveBeenCalledWith(editor); 107 | }); 108 | 109 | it('syncs the visibleMarkerLayer from selection manager to the scroll marker api', () => { 110 | spyOn(markerView, 'getLayer').andCallThrough(); 111 | spyOn(layer, 'syncToMarkerLayer'); 112 | 113 | scrollMarkersService.setScrollMarkerView(editor); 114 | 115 | expect(markerView.getLayer).toHaveBeenCalledWith('highlight-selected-marker-layer'); 116 | expect(layer.syncToMarkerLayer).toHaveBeenCalledWith(visibleMarkerLayer); 117 | }); 118 | 119 | it('syncs the selectedMarkerLayer from selection manager to the scroll marker api', () => { 120 | spyOn(markerView, 'getLayer').andCallThrough(); 121 | spyOn(layer, 'syncToMarkerLayer'); 122 | 123 | scrollMarkersService.setScrollMarkerView(editor); 124 | 125 | expect(markerView.getLayer).toHaveBeenCalledWith('highlight-selected-selected-marker-layer'); 126 | expect(layer.syncToMarkerLayer).toHaveBeenCalledWith(selectedMarkerLayer); 127 | }); 128 | }); 129 | 130 | describe('setupConfigObserver', () => { 131 | beforeEach(() => { 132 | createScrollMarkersService(); 133 | }); 134 | 135 | describe('when enabling show results on scroll bar', () => { 136 | beforeEach(() => { 137 | atom.config.set('highlight-selected.showResultsOnScrollBar', false); 138 | }); 139 | 140 | it('ensures the scroll bar package is installed', () => { 141 | spyOn(ScrollMarkersService, 'ensureScrollViewInstalled'); 142 | atom.config.set('highlight-selected.showResultsOnScrollBar', true); 143 | 144 | expect(ScrollMarkersService.ensureScrollViewInstalled).toHaveBeenCalled(); 145 | }); 146 | 147 | it('sets scroll marker view for any open editor', () => { 148 | atom.project.setPaths([path.join(__dirname, 'fixtures')]); 149 | waitsForPromise(() => atom.workspace.open('sample.php')); 150 | 151 | runs(() => { 152 | const editor = atom.workspace.getActiveTextEditor(); 153 | spyOn(scrollMarkersService, 'setScrollMarkerView'); 154 | atom.config.set('highlight-selected.showResultsOnScrollBar', true); 155 | expect(scrollMarkersService.setScrollMarkerView).toHaveBeenCalledWith(editor); 156 | }); 157 | }); 158 | }); 159 | 160 | describe('when disabling show results on scroll bar', () => { 161 | beforeEach(() => { 162 | atom.config.set('highlight-selected.showResultsOnScrollBar', true); 163 | // Turn off the editor subscriptions otherwise everything breaks as we have stubbed out the 164 | // Scroll Marker API. We do not want to actually run the thing. 165 | scrollMarkersService.editorSubscriptions.dispose(); 166 | }); 167 | 168 | it('destroys scroll markers for open editors', () => { 169 | atom.project.setPaths([path.join(__dirname, 'fixtures')]); 170 | waitsForPromise(() => atom.workspace.open('sample.php')); 171 | 172 | runs(() => { 173 | const editor = atom.workspace.getActiveTextEditor(); 174 | spyOn(scrollMarkersService, 'destroyScrollMarkers'); 175 | atom.config.set('highlight-selected.showResultsOnScrollBar', false); 176 | expect(scrollMarkersService.destroyScrollMarkers).toHaveBeenCalledWith(editor); 177 | }); 178 | }); 179 | }); 180 | }); 181 | }); 182 | -------------------------------------------------------------------------------- /Tests/status-bar/status-bar-service-spec.js: -------------------------------------------------------------------------------- 1 | const StatusBarService = require('../../Source/status-bar/status-bar-service'); 2 | 3 | describe('StatusBarService', () => { 4 | let statusBarService; 5 | let selectionManager; 6 | let statusBarApi; 7 | const tile = { destroy: () => {} }; 8 | 9 | const createStatusBarService = () => { 10 | statusBarApi = { 11 | addLeftTile: () => tile, 12 | }; 13 | spyOn(statusBarApi, 'addLeftTile').andCallThrough(); 14 | selectionManager = { 15 | onDidFinishAddingMarkers: () => {}, 16 | onDidRemoveAllMarkers: () => {}, 17 | }; 18 | spyOn(selectionManager, 'onDidFinishAddingMarkers'); 19 | spyOn(selectionManager, 'onDidRemoveAllMarkers'); 20 | statusBarService = new StatusBarService(statusBarApi, selectionManager); 21 | }; 22 | 23 | beforeEach(() => { 24 | waitsForPromise(() => atom.packages.activatePackage('highlight-selected')); 25 | waitsForPromise(() => atom.packages.activatePackage('status-bar')); 26 | }); 27 | 28 | it('listens for once all markers are added', () => { 29 | createStatusBarService(); 30 | expect(selectionManager.onDidFinishAddingMarkers).toHaveBeenCalled(); 31 | }); 32 | 33 | it('listens for for when all markers have been removed', () => { 34 | createStatusBarService(); 35 | expect(selectionManager.onDidRemoveAllMarkers).toHaveBeenCalled(); 36 | }); 37 | 38 | describe('destroy', () => { 39 | let selectionSubscriptionSpy; 40 | 41 | beforeEach(() => { 42 | createStatusBarService(); 43 | selectionSubscriptionSpy = { 44 | dispose: () => {}, 45 | }; 46 | spyOn(selectionSubscriptionSpy, 'dispose'); 47 | statusBarService.selectionSubscription = selectionSubscriptionSpy; 48 | spyOn(statusBarService, 'removeStatusBarView'); 49 | }); 50 | 51 | it('removes subscriptions', () => { 52 | statusBarService.destroy(); 53 | expect(selectionSubscriptionSpy.dispose).toHaveBeenCalled(); 54 | }); 55 | 56 | it('removes status bar', () => { 57 | statusBarService.destroy(); 58 | expect(statusBarService.removeStatusBarView).toHaveBeenCalled(); 59 | }); 60 | }); 61 | 62 | describe('listenForStatusBarConfigChange', () => { 63 | beforeEach(() => { 64 | createStatusBarService(); 65 | }); 66 | 67 | describe('when it is enabled', () => { 68 | beforeEach(() => { 69 | atom.config.set('highlight-selected.showInStatusBar', true); 70 | const selectionSubscriptionSpy = { 71 | dispose: () => {}, 72 | }; 73 | spyOn(selectionSubscriptionSpy, 'dispose'); 74 | statusBarService.selectionSubscription = selectionSubscriptionSpy; 75 | }); 76 | 77 | it('calls removeStatusBarView when set to false', () => { 78 | spyOn(statusBarService, 'removeStatusBarView'); 79 | atom.config.set('highlight-selected.showInStatusBar', false); 80 | expect(statusBarService.removeStatusBarView).toHaveBeenCalled(); 81 | }); 82 | }); 83 | 84 | describe('when it is disabled', () => { 85 | beforeEach(() => { 86 | spyOn(statusBarService, 'removeStatusBarView'); 87 | atom.config.set('highlight-selected.showInStatusBar', false); 88 | }); 89 | 90 | it('calls setupStatusBarView when set to true', () => { 91 | spyOn(statusBarService, 'setupStatusBarView'); 92 | atom.config.set('highlight-selected.showInStatusBar', true); 93 | expect(statusBarService.setupStatusBarView).toHaveBeenCalled(); 94 | }); 95 | }); 96 | }); 97 | 98 | describe('setupStatusBarView', () => { 99 | describe('when we already have an element setup', () => { 100 | const element = { 101 | temp: 'Object', 102 | }; 103 | 104 | beforeEach(() => { 105 | createStatusBarService(); 106 | statusBarService.statusBarElement = element; 107 | }); 108 | 109 | it('does not overwrite element', () => { 110 | statusBarService.setupStatusBarView(); 111 | expect(statusBarService.statusBarElement).toBe(element); 112 | }); 113 | }); 114 | 115 | describe('when we have not enabled the status bar', () => { 116 | beforeEach(() => { 117 | atom.config.set('highlight-selected.showInStatusBar', false); 118 | createStatusBarService(); 119 | }); 120 | 121 | it('does not have an element', () => { 122 | expect(statusBarService.statusBarElement).toBeUndefined(); 123 | }); 124 | }); 125 | 126 | describe('when we have enabled the status bar', () => { 127 | beforeEach(() => { 128 | atom.config.set('highlight-selected.showInStatusBar', true); 129 | createStatusBarService(); 130 | }); 131 | 132 | it('adds the status bar to the left hand side', () => { 133 | expect(statusBarApi.addLeftTile).toHaveBeenCalledWith({ 134 | item: statusBarService.statusBarElement.element , 135 | priority: 100, 136 | }); 137 | }); 138 | }); 139 | }); 140 | 141 | describe('removeStatusBarView', () => { 142 | beforeEach(() => { 143 | atom.config.set('highlight-selected.showInStatusBar', true); 144 | createStatusBarService(); 145 | }); 146 | 147 | describe('when there is not an element', () => { 148 | beforeEach(() => { 149 | statusBarService.statusBarElement = null; 150 | }); 151 | 152 | it('does not destroy the tile', () => { 153 | spyOn(tile, 'destroy'); 154 | statusBarService.removeStatusBarView(); 155 | expect(tile.destroy).not.toHaveBeenCalled(); 156 | }); 157 | }); 158 | 159 | describe('when there is an element', () => { 160 | let statusBarElement; 161 | 162 | beforeEach(() => { 163 | ({ statusBarElement } = statusBarService); 164 | spyOn(statusBarElement, 'removeElement'); 165 | }); 166 | 167 | it('removes the element', () => { 168 | statusBarService.removeStatusBarView(); 169 | expect(statusBarElement.removeElement).toHaveBeenCalled(); 170 | }); 171 | 172 | it('destroys the tile', () => { 173 | spyOn(tile, 'destroy'); 174 | statusBarService.removeStatusBarView(); 175 | expect(tile.destroy).toHaveBeenCalled(); 176 | }); 177 | 178 | it('sets the tile to be null', () => { 179 | expect(statusBarService.statusBarTile).not.toBeNull(); 180 | statusBarService.removeStatusBarView(); 181 | expect(statusBarService.statusBarTile).toBeNull(); 182 | }); 183 | 184 | it('sets the element to be null', () => { 185 | expect(statusBarService.statusBarElement).not.toBeNull(); 186 | statusBarService.removeStatusBarView(); 187 | expect(statusBarService.statusBarElement).toBeNull(); 188 | }); 189 | }); 190 | }); 191 | 192 | describe('updateCount', () => { 193 | beforeEach(() => { 194 | createStatusBarService(); 195 | }); 196 | 197 | describe('when there is no element', () => { 198 | beforeEach(() => { 199 | statusBarService.statusBarElement = null; 200 | }); 201 | 202 | it('does not error', () => { 203 | statusBarService.updateCount(); 204 | }); 205 | }); 206 | 207 | it('calls the updateCount function on the element', () => { 208 | const { statusBarElement } = statusBarService; 209 | spyOn(statusBarElement, 'updateCount'); 210 | statusBarService.updateCount(); 211 | expect(statusBarElement.updateCount).toHaveBeenCalled(); 212 | }); 213 | 214 | it("uses the selection manager's resultCount", () => { 215 | const number = 123; 216 | selectionManager.resultCount = number; 217 | const { statusBarElement } = statusBarService; 218 | spyOn(statusBarElement, 'updateCount'); 219 | statusBarService.updateCount(); 220 | expect(statusBarElement.updateCount).toHaveBeenCalledWith(number); 221 | }); 222 | }); 223 | }); 224 | -------------------------------------------------------------------------------- /Tests/status-bar/status-bar-view-spec.js: -------------------------------------------------------------------------------- 1 | const StatusBarView = require('../../Source/status-bar/status-bar-view'); 2 | 3 | describe('StatusBarView', () => { 4 | let statusBarView; 5 | 6 | beforeEach(() => { 7 | waitsForPromise(() => atom.packages.activatePackage('highlight-selected')); 8 | statusBarView = new StatusBarView(); 9 | }); 10 | 11 | it('creates a div element', () => { 12 | expect(statusBarView.element.tagName).toBe('DIV'); 13 | }); 14 | 15 | it('div contains correct classes', () => { 16 | expect(statusBarView.element).toHaveClass('highlight-selected-status'); 17 | expect(statusBarView.element).toHaveClass('inline-block'); 18 | }); 19 | 20 | it('returns element', () => { 21 | expect(statusBarView.element).toBe(statusBarView.element); 22 | }); 23 | 24 | describe('updateCount', () => { 25 | it('updates with 0', () => { 26 | const expectedText = 'Highlighted: 0'; 27 | statusBarView.updateCount(0); 28 | expect(statusBarView.element).toHaveClass('highlight-selected-hidden'); 29 | expect(statusBarView.element).toHaveText(expectedText); 30 | }); 31 | 32 | it('updates with 1', () => { 33 | const expectedText = 'Highlighted: 1'; 34 | statusBarView.updateCount(1); 35 | expect(statusBarView.element).not.toHaveClass('highlight-selected-hidden'); 36 | expect(statusBarView.element).toHaveText(expectedText); 37 | }); 38 | 39 | it('updates with 55', () => { 40 | const expectedText = 'Highlighted: 55'; 41 | statusBarView.updateCount(55); 42 | expect(statusBarView.element).not.toHaveClass('highlight-selected-hidden'); 43 | expect(statusBarView.element).toHaveText(expectedText); 44 | }); 45 | }); 46 | 47 | it('removes the element from the parent', () => { 48 | const parentNode = { removeChild: () => {} }; 49 | const element = { parentNode }; 50 | 51 | spyOn(parentNode, 'removeChild'); 52 | 53 | statusBarView.element = element; 54 | statusBarView.removeElement(); 55 | 56 | expect(parentNode.removeChild).toHaveBeenCalledWith(element); 57 | expect(statusBarView.element).toBeNull(); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /Tests/utils/non-word-characters-spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const getNonWordCharacters = require('../../Source/utils/non-word-characters.js'); 3 | 4 | describe('with a CoffeeScript file', () => { 5 | const nonWordCharacters = '{}[]<>'; 6 | let workspaceElement; 7 | let editor; 8 | let selectionStart; 9 | 10 | beforeEach(() => { 11 | // Need this package to be active otherwise we do not use the source in the 12 | // `getNonWordCharacters` function 13 | waitsForPromise(() => atom.packages.activatePackage('language-coffee-script')); 14 | 15 | atom.config.set('editor.nonWordCharacters', nonWordCharacters, { 16 | scopeSelector: '.source.coffee', 17 | }); 18 | 19 | workspaceElement = atom.views.getView(atom.workspace); 20 | atom.project.setPaths([path.join('..', '..', 'fixtures')]); 21 | 22 | waitsForPromise(() => atom.workspace.open('sample.coffee')); 23 | 24 | runs(() => { 25 | jasmine.attachToDOM(workspaceElement); 26 | editor = atom.workspace.getActiveTextEditor(); 27 | const editorElement = atom.views.getView(editor); 28 | editorElement.setHeight(250); 29 | editorElement.component.measureDimensions(); 30 | selectionStart = editor.getLastSelection().getBufferRange().start; 31 | }); 32 | }); 33 | 34 | it('returns differently than the editor.nonWordCharacters', () => { 35 | expect(getNonWordCharacters(editor, selectionStart)).not.toBe( 36 | atom.config.get('editor.nonWordCharacters') 37 | ); 38 | }); 39 | 40 | it('returns the correct non word characters', () => { 41 | expect(getNonWordCharacters(editor, selectionStart)).toBe(nonWordCharacters); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "description" : "Highlights the current word selected when double clicking" , 3 | "version" : "1.0.0" , 4 | "license" : "MIT" , 5 | "name" : "highlight-selected" , 6 | 7 | "keywords" : [ "highlight" , "select" , "occurrence" , "mark" ] , 8 | 9 | "homepage" : "https://github.com/Pulsar-Edit-Highlights/selected" , 10 | 11 | "repository" : { 12 | "type" : "git" , 13 | "url" : "https://github.com/Pulsar-Edit-Highlights/selected" 14 | }, 15 | 16 | "bugs" : { 17 | "url" : "https://github.com/Pulsar-Edit-Highlights/selected/issues" 18 | }, 19 | 20 | "mainStyleSheet" : "./Resources/Stylesheet.less" , 21 | "main" : "./Source/App.js" , 22 | 23 | "keymaps" : [ "../Resources/Keymap.json" ] , 24 | "menus" : [ "../Resources/Menus.json" ] , 25 | 26 | "engines" : { 27 | "pulsar" : ">=1.100.0 <2.0.0" 28 | }, 29 | 30 | "scripts" : { 31 | "test" : "pulsar --test Tests" 32 | }, 33 | 34 | "dependencies" : { 35 | "atom-package-deps" : "^7.0.0" , 36 | "debounce" : "^1.2.0" , 37 | "grim" : "^2.0.2" 38 | }, 39 | 40 | 41 | "providedServices" : { 42 | "highlightSelected" : { 43 | 44 | "description" : "Exposes Highlight Selected Events" , 45 | 46 | "versions" : { 47 | "1.0.0" : "provideHighlightSelectedV1Deprecated" , 48 | "2.0.0" : "provideHighlightSelectedV2" 49 | } 50 | } 51 | }, 52 | 53 | "consumedServices" : { 54 | 55 | "status-bar" : { 56 | "versions" : { 57 | "^1.0.0" : "consumeStatusBar" 58 | } 59 | }, 60 | 61 | "scroll-marker" : { 62 | "versions" : { 63 | "0.1.0" : "consumeScrollMarker" 64 | } 65 | } 66 | }, 67 | 68 | "package-deps" : [{ "name" : "scroll-marker" }], 69 | 70 | "configSchema" : { 71 | 72 | "onlyHighlightWholeWords" : { 73 | "default" : true , 74 | "type" : "boolean" 75 | }, 76 | 77 | "hideHighlightOnSelectedWord" : { 78 | "default" : false , 79 | "type" : "boolean" 80 | }, 81 | 82 | "ignoreCase" : { 83 | "default" : false , 84 | "type" : "boolean" 85 | }, 86 | 87 | "lightTheme" : { 88 | "default" : false , 89 | "type" : "boolean" 90 | }, 91 | 92 | "highlightBackground" : { 93 | "default" : false , 94 | "type" : "boolean" 95 | }, 96 | 97 | "minimumLength" : { 98 | "default" : 2 , 99 | "type" : "integer" 100 | }, 101 | 102 | "maximumHighlights" : { 103 | "description" : "For performance purposes, the number of highlights is limited." , 104 | "default" : 500 , 105 | "type" : "integer" 106 | }, 107 | 108 | "timeout" : { 109 | "description" : "Defers searching for matching strings for X ms." , 110 | "default" : 20 , 111 | "type" : "integer" 112 | }, 113 | 114 | "showInStatusBar" : { 115 | "description" : "Show how many matches there are." , 116 | "default" : true , 117 | "type" : "boolean" 118 | }, 119 | 120 | "highlightInPanes" : { 121 | "description" : "Highlight selection in another panes." , 122 | "default" : true , 123 | "type" : "boolean" 124 | }, 125 | 126 | "statusBarString" : { 127 | "description" : "The text to show in the status bar. %c = number of occurrences." , 128 | "default" : "Highlighted: %c" , 129 | "type" : "string" 130 | }, 131 | 132 | "allowedCharactersToSelect" : { 133 | "description" : "Non Word Characters that are allowed to be selected." , 134 | "default" : "$@%-" , 135 | "type" : "string" 136 | }, 137 | 138 | "showResultsOnScrollBar" : { 139 | "description" : "Show highlight on the scroll bar." , 140 | "default" : false , 141 | "type" : "boolean" 142 | } 143 | } 144 | } 145 | --------------------------------------------------------------------------------