From 100b39b95d6250d187a491adfd1e92a29f1f841b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Beauducel?= Date: Sat, 13 Apr 2024 14:09:43 +0200 Subject: [PATCH] removes unused files in js/codemirror --- CODE/js/codemirror/AUTHORS | 901 --- CODE/js/codemirror/CHANGELOG.md | 1912 ------- CODE/js/codemirror/CONTRIBUTING.md | 92 - CODE/js/codemirror/LICENSE | 21 - CODE/js/codemirror/README.md | 47 - CODE/js/codemirror/bin/authors.sh | 6 - CODE/js/codemirror/bin/lint | 3 - CODE/js/codemirror/bin/release | 38 - CODE/js/codemirror/bin/source-highlight | 48 - CODE/js/codemirror/bin/upload-release.js | 35 - CODE/js/codemirror/demo/activeline.html | 88 - CODE/js/codemirror/demo/anywordhint.html | 79 - CODE/js/codemirror/demo/bidi.html | 106 - CODE/js/codemirror/demo/btree.html | 83 - CODE/js/codemirror/demo/buffers.html | 109 - CODE/js/codemirror/demo/changemode.html | 58 - CODE/js/codemirror/demo/closebrackets.html | 52 - CODE/js/codemirror/demo/closetag.html | 41 - CODE/js/codemirror/demo/complete.html | 126 - CODE/js/codemirror/demo/emacs.html | 76 - CODE/js/codemirror/demo/folding.html | 184 - CODE/js/codemirror/demo/fullscreen.html | 83 - CODE/js/codemirror/demo/hardwrap.html | 75 - CODE/js/codemirror/demo/html5complete.html | 56 - CODE/js/codemirror/demo/indentwrap.html | 59 - CODE/js/codemirror/demo/lint.html | 171 - CODE/js/codemirror/demo/loadmode.html | 72 - CODE/js/codemirror/demo/marker.html | 52 - CODE/js/codemirror/demo/markselection.html | 52 - CODE/js/codemirror/demo/matchhighlighter.html | 103 - CODE/js/codemirror/demo/matchtags.html | 48 - CODE/js/codemirror/demo/merge.html | 123 - CODE/js/codemirror/demo/multiplex.html | 75 - CODE/js/codemirror/demo/mustache.html | 69 - CODE/js/codemirror/demo/panel.html | 137 - CODE/js/codemirror/demo/placeholder.html | 45 - CODE/js/codemirror/demo/preview.html | 87 - CODE/js/codemirror/demo/requirejs.html | 70 - CODE/js/codemirror/demo/resize.html | 51 - CODE/js/codemirror/demo/rulers.html | 49 - .../codemirror/demo/runmode-standalone.html | 61 - CODE/js/codemirror/demo/runmode.html | 62 - CODE/js/codemirror/demo/search.html | 99 - CODE/js/codemirror/demo/simplemode.html | 186 - CODE/js/codemirror/demo/simplescrollbars.html | 82 - .../demo/spanaffectswrapping_shim.html | 85 - CODE/js/codemirror/demo/sublime.html | 77 - CODE/js/codemirror/demo/tern.html | 133 - CODE/js/codemirror/demo/theme.html | 196 - CODE/js/codemirror/demo/trailingspace.html | 48 - CODE/js/codemirror/demo/variableheight.html | 67 - CODE/js/codemirror/demo/vim.html | 117 - CODE/js/codemirror/demo/visibletabs.html | 62 - CODE/js/codemirror/demo/widget.html | 85 - CODE/js/codemirror/demo/xmlcomplete.html | 119 - CODE/js/codemirror/doc/activebookmark.js | 57 - CODE/js/codemirror/doc/docs.css | 274 - CODE/js/codemirror/doc/internals.html | 504 -- CODE/js/codemirror/doc/logo.png | Bin 9310 -> 0 bytes CODE/js/codemirror/doc/logo.svg | 19 - CODE/js/codemirror/doc/manual.html | 3749 ------------ CODE/js/codemirror/doc/realworld.html | 205 - CODE/js/codemirror/doc/releases.html | 1967 ------- CODE/js/codemirror/doc/reporting.html | 60 - CODE/js/codemirror/doc/upgrade_v2.2.html | 96 - CODE/js/codemirror/doc/upgrade_v3.html | 230 - CODE/js/codemirror/doc/upgrade_v4.html | 144 - CODE/js/codemirror/doc/yinyang.png | Bin 4633 -> 0 bytes CODE/js/codemirror/index.html | 210 - CODE/js/codemirror/package.json | 47 - CODE/js/codemirror/rollup.config.js | 42 - .../addon/runmode/codemirror-standalone.js | 22 - .../src/addon/runmode/codemirror.node.js | 21 - .../src/addon/runmode/runmode-standalone.js | 2 - .../src/addon/runmode/runmode.node.js | 2 - CODE/js/codemirror/src/codemirror.js | 3 - CODE/js/codemirror/src/display/Display.js | 110 - CODE/js/codemirror/src/display/focus.js | 50 - CODE/js/codemirror/src/display/gutters.js | 44 - .../src/display/highlight_worker.js | 55 - .../js/codemirror/src/display/line_numbers.js | 48 - CODE/js/codemirror/src/display/mode_state.js | 22 - CODE/js/codemirror/src/display/operations.js | 205 - .../codemirror/src/display/scroll_events.js | 115 - CODE/js/codemirror/src/display/scrollbars.js | 193 - CODE/js/codemirror/src/display/scrolling.js | 185 - CODE/js/codemirror/src/display/selection.js | 161 - .../codemirror/src/display/update_display.js | 263 - CODE/js/codemirror/src/display/update_line.js | 188 - .../js/codemirror/src/display/update_lines.js | 76 - .../codemirror/src/display/view_tracking.js | 153 - CODE/js/codemirror/src/edit/CodeMirror.js | 217 - CODE/js/codemirror/src/edit/commands.js | 178 - .../src/edit/deleteNearSelection.js | 30 - CODE/js/codemirror/src/edit/drop_events.js | 130 - CODE/js/codemirror/src/edit/fromTextArea.js | 61 - CODE/js/codemirror/src/edit/global_events.js | 45 - CODE/js/codemirror/src/edit/key_events.js | 163 - CODE/js/codemirror/src/edit/legacy.js | 62 - CODE/js/codemirror/src/edit/main.js | 69 - CODE/js/codemirror/src/edit/methods.js | 552 -- CODE/js/codemirror/src/edit/mouse_events.js | 417 -- CODE/js/codemirror/src/edit/options.js | 194 - CODE/js/codemirror/src/edit/utils.js | 7 - .../src/input/ContentEditableInput.js | 544 -- CODE/js/codemirror/src/input/TextareaInput.js | 375 -- CODE/js/codemirror/src/input/indent.js | 71 - CODE/js/codemirror/src/input/input.js | 135 - CODE/js/codemirror/src/input/keymap.js | 148 - CODE/js/codemirror/src/input/keynames.js | 17 - CODE/js/codemirror/src/input/movement.js | 111 - CODE/js/codemirror/src/line/highlight.js | 284 - CODE/js/codemirror/src/line/line_data.js | 349 -- CODE/js/codemirror/src/line/pos.js | 40 - .../codemirror/src/line/saw_special_spans.js | 10 - CODE/js/codemirror/src/line/spans.js | 382 -- CODE/js/codemirror/src/line/utils_line.js | 85 - .../src/measurement/position_measurement.js | 700 --- CODE/js/codemirror/src/measurement/widgets.js | 26 - CODE/js/codemirror/src/model/Doc.js | 435 -- .../src/model/change_measurement.js | 61 - CODE/js/codemirror/src/model/changes.js | 339 -- CODE/js/codemirror/src/model/chunk.js | 167 - CODE/js/codemirror/src/model/document_data.js | 111 - CODE/js/codemirror/src/model/history.js | 228 - CODE/js/codemirror/src/model/line_widget.js | 78 - CODE/js/codemirror/src/model/mark_text.js | 293 - CODE/js/codemirror/src/model/selection.js | 84 - .../codemirror/src/model/selection_updates.js | 216 - CODE/js/codemirror/src/modes.js | 96 - CODE/js/codemirror/src/util/StringStream.js | 90 - CODE/js/codemirror/src/util/bidi.js | 215 - CODE/js/codemirror/src/util/browser.js | 33 - CODE/js/codemirror/src/util/dom.js | 97 - CODE/js/codemirror/src/util/event.js | 103 - .../codemirror/src/util/feature_detection.js | 84 - CODE/js/codemirror/src/util/misc.js | 168 - .../js/codemirror/src/util/operation_group.js | 72 - CODE/js/codemirror/test/annotatescrollbar.js | 55 - CODE/js/codemirror/test/comment_test.js | 114 - .../codemirror/test/contenteditable_test.js | 110 - CODE/js/codemirror/test/doc_test.js | 371 -- CODE/js/codemirror/test/driver.js | 141 - CODE/js/codemirror/test/emacs_test.js | 149 - CODE/js/codemirror/test/html-hint-test.js | 83 - CODE/js/codemirror/test/index.html | 291 - CODE/js/codemirror/test/lint.js | 20 - CODE/js/codemirror/test/mode_test.css | 23 - CODE/js/codemirror/test/mode_test.js | 193 - CODE/js/codemirror/test/multi_test.js | 295 - CODE/js/codemirror/test/run.js | 41 - CODE/js/codemirror/test/scroll_test.js | 126 - CODE/js/codemirror/test/search_test.js | 91 - CODE/js/codemirror/test/sql-hint-test.js | 301 - CODE/js/codemirror/test/sublime_test.js | 295 - CODE/js/codemirror/test/test.js | 2674 --------- CODE/js/codemirror/test/vim_test.js | 5079 ----------------- 157 files changed, 35407 deletions(-) delete mode 100644 CODE/js/codemirror/AUTHORS delete mode 100644 CODE/js/codemirror/CHANGELOG.md delete mode 100644 CODE/js/codemirror/CONTRIBUTING.md delete mode 100644 CODE/js/codemirror/LICENSE delete mode 100644 CODE/js/codemirror/README.md delete mode 100755 CODE/js/codemirror/bin/authors.sh delete mode 100755 CODE/js/codemirror/bin/lint delete mode 100755 CODE/js/codemirror/bin/release delete mode 100755 CODE/js/codemirror/bin/source-highlight delete mode 100644 CODE/js/codemirror/bin/upload-release.js delete mode 100644 CODE/js/codemirror/demo/activeline.html delete mode 100644 CODE/js/codemirror/demo/anywordhint.html delete mode 100644 CODE/js/codemirror/demo/bidi.html delete mode 100644 CODE/js/codemirror/demo/btree.html delete mode 100644 CODE/js/codemirror/demo/buffers.html delete mode 100644 CODE/js/codemirror/demo/changemode.html delete mode 100644 CODE/js/codemirror/demo/closebrackets.html delete mode 100644 CODE/js/codemirror/demo/closetag.html delete mode 100644 CODE/js/codemirror/demo/complete.html delete mode 100644 CODE/js/codemirror/demo/emacs.html delete mode 100644 CODE/js/codemirror/demo/folding.html delete mode 100644 CODE/js/codemirror/demo/fullscreen.html delete mode 100644 CODE/js/codemirror/demo/hardwrap.html delete mode 100644 CODE/js/codemirror/demo/html5complete.html delete mode 100644 CODE/js/codemirror/demo/indentwrap.html delete mode 100644 CODE/js/codemirror/demo/lint.html delete mode 100644 CODE/js/codemirror/demo/loadmode.html delete mode 100644 CODE/js/codemirror/demo/marker.html delete mode 100644 CODE/js/codemirror/demo/markselection.html delete mode 100644 CODE/js/codemirror/demo/matchhighlighter.html delete mode 100644 CODE/js/codemirror/demo/matchtags.html delete mode 100644 CODE/js/codemirror/demo/merge.html delete mode 100644 CODE/js/codemirror/demo/multiplex.html delete mode 100644 CODE/js/codemirror/demo/mustache.html delete mode 100644 CODE/js/codemirror/demo/panel.html delete mode 100644 CODE/js/codemirror/demo/placeholder.html delete mode 100644 CODE/js/codemirror/demo/preview.html delete mode 100644 CODE/js/codemirror/demo/requirejs.html delete mode 100644 CODE/js/codemirror/demo/resize.html delete mode 100644 CODE/js/codemirror/demo/rulers.html delete mode 100644 CODE/js/codemirror/demo/runmode-standalone.html delete mode 100644 CODE/js/codemirror/demo/runmode.html delete mode 100644 CODE/js/codemirror/demo/search.html delete mode 100644 CODE/js/codemirror/demo/simplemode.html delete mode 100644 CODE/js/codemirror/demo/simplescrollbars.html delete mode 100644 CODE/js/codemirror/demo/spanaffectswrapping_shim.html delete mode 100644 CODE/js/codemirror/demo/sublime.html delete mode 100644 CODE/js/codemirror/demo/tern.html delete mode 100644 CODE/js/codemirror/demo/theme.html delete mode 100644 CODE/js/codemirror/demo/trailingspace.html delete mode 100644 CODE/js/codemirror/demo/variableheight.html delete mode 100644 CODE/js/codemirror/demo/vim.html delete mode 100644 CODE/js/codemirror/demo/visibletabs.html delete mode 100644 CODE/js/codemirror/demo/widget.html delete mode 100644 CODE/js/codemirror/demo/xmlcomplete.html delete mode 100644 CODE/js/codemirror/doc/activebookmark.js delete mode 100644 CODE/js/codemirror/doc/docs.css delete mode 100644 CODE/js/codemirror/doc/internals.html delete mode 100644 CODE/js/codemirror/doc/logo.png delete mode 100644 CODE/js/codemirror/doc/logo.svg delete mode 100644 CODE/js/codemirror/doc/manual.html delete mode 100644 CODE/js/codemirror/doc/realworld.html delete mode 100644 CODE/js/codemirror/doc/releases.html delete mode 100644 CODE/js/codemirror/doc/reporting.html delete mode 100644 CODE/js/codemirror/doc/upgrade_v2.2.html delete mode 100644 CODE/js/codemirror/doc/upgrade_v3.html delete mode 100644 CODE/js/codemirror/doc/upgrade_v4.html delete mode 100644 CODE/js/codemirror/doc/yinyang.png delete mode 100644 CODE/js/codemirror/index.html delete mode 100644 CODE/js/codemirror/package.json delete mode 100644 CODE/js/codemirror/rollup.config.js delete mode 100644 CODE/js/codemirror/src/addon/runmode/codemirror-standalone.js delete mode 100644 CODE/js/codemirror/src/addon/runmode/codemirror.node.js delete mode 100644 CODE/js/codemirror/src/addon/runmode/runmode-standalone.js delete mode 100644 CODE/js/codemirror/src/addon/runmode/runmode.node.js delete mode 100644 CODE/js/codemirror/src/codemirror.js delete mode 100644 CODE/js/codemirror/src/display/Display.js delete mode 100644 CODE/js/codemirror/src/display/focus.js delete mode 100644 CODE/js/codemirror/src/display/gutters.js delete mode 100644 CODE/js/codemirror/src/display/highlight_worker.js delete mode 100644 CODE/js/codemirror/src/display/line_numbers.js delete mode 100644 CODE/js/codemirror/src/display/mode_state.js delete mode 100644 CODE/js/codemirror/src/display/operations.js delete mode 100644 CODE/js/codemirror/src/display/scroll_events.js delete mode 100644 CODE/js/codemirror/src/display/scrollbars.js delete mode 100644 CODE/js/codemirror/src/display/scrolling.js delete mode 100644 CODE/js/codemirror/src/display/selection.js delete mode 100644 CODE/js/codemirror/src/display/update_display.js delete mode 100644 CODE/js/codemirror/src/display/update_line.js delete mode 100644 CODE/js/codemirror/src/display/update_lines.js delete mode 100644 CODE/js/codemirror/src/display/view_tracking.js delete mode 100644 CODE/js/codemirror/src/edit/CodeMirror.js delete mode 100644 CODE/js/codemirror/src/edit/commands.js delete mode 100644 CODE/js/codemirror/src/edit/deleteNearSelection.js delete mode 100644 CODE/js/codemirror/src/edit/drop_events.js delete mode 100644 CODE/js/codemirror/src/edit/fromTextArea.js delete mode 100644 CODE/js/codemirror/src/edit/global_events.js delete mode 100644 CODE/js/codemirror/src/edit/key_events.js delete mode 100644 CODE/js/codemirror/src/edit/legacy.js delete mode 100644 CODE/js/codemirror/src/edit/main.js delete mode 100644 CODE/js/codemirror/src/edit/methods.js delete mode 100644 CODE/js/codemirror/src/edit/mouse_events.js delete mode 100644 CODE/js/codemirror/src/edit/options.js delete mode 100644 CODE/js/codemirror/src/edit/utils.js delete mode 100644 CODE/js/codemirror/src/input/ContentEditableInput.js delete mode 100644 CODE/js/codemirror/src/input/TextareaInput.js delete mode 100644 CODE/js/codemirror/src/input/indent.js delete mode 100644 CODE/js/codemirror/src/input/input.js delete mode 100644 CODE/js/codemirror/src/input/keymap.js delete mode 100644 CODE/js/codemirror/src/input/keynames.js delete mode 100644 CODE/js/codemirror/src/input/movement.js delete mode 100644 CODE/js/codemirror/src/line/highlight.js delete mode 100644 CODE/js/codemirror/src/line/line_data.js delete mode 100644 CODE/js/codemirror/src/line/pos.js delete mode 100644 CODE/js/codemirror/src/line/saw_special_spans.js delete mode 100644 CODE/js/codemirror/src/line/spans.js delete mode 100644 CODE/js/codemirror/src/line/utils_line.js delete mode 100644 CODE/js/codemirror/src/measurement/position_measurement.js delete mode 100644 CODE/js/codemirror/src/measurement/widgets.js delete mode 100644 CODE/js/codemirror/src/model/Doc.js delete mode 100644 CODE/js/codemirror/src/model/change_measurement.js delete mode 100644 CODE/js/codemirror/src/model/changes.js delete mode 100644 CODE/js/codemirror/src/model/chunk.js delete mode 100644 CODE/js/codemirror/src/model/document_data.js delete mode 100644 CODE/js/codemirror/src/model/history.js delete mode 100644 CODE/js/codemirror/src/model/line_widget.js delete mode 100644 CODE/js/codemirror/src/model/mark_text.js delete mode 100644 CODE/js/codemirror/src/model/selection.js delete mode 100644 CODE/js/codemirror/src/model/selection_updates.js delete mode 100644 CODE/js/codemirror/src/modes.js delete mode 100644 CODE/js/codemirror/src/util/StringStream.js delete mode 100644 CODE/js/codemirror/src/util/bidi.js delete mode 100644 CODE/js/codemirror/src/util/browser.js delete mode 100644 CODE/js/codemirror/src/util/dom.js delete mode 100644 CODE/js/codemirror/src/util/event.js delete mode 100644 CODE/js/codemirror/src/util/feature_detection.js delete mode 100644 CODE/js/codemirror/src/util/misc.js delete mode 100644 CODE/js/codemirror/src/util/operation_group.js delete mode 100644 CODE/js/codemirror/test/annotatescrollbar.js delete mode 100644 CODE/js/codemirror/test/comment_test.js delete mode 100644 CODE/js/codemirror/test/contenteditable_test.js delete mode 100644 CODE/js/codemirror/test/doc_test.js delete mode 100644 CODE/js/codemirror/test/driver.js delete mode 100644 CODE/js/codemirror/test/emacs_test.js delete mode 100644 CODE/js/codemirror/test/html-hint-test.js delete mode 100644 CODE/js/codemirror/test/index.html delete mode 100644 CODE/js/codemirror/test/lint.js delete mode 100644 CODE/js/codemirror/test/mode_test.css delete mode 100644 CODE/js/codemirror/test/mode_test.js delete mode 100644 CODE/js/codemirror/test/multi_test.js delete mode 100755 CODE/js/codemirror/test/run.js delete mode 100644 CODE/js/codemirror/test/scroll_test.js delete mode 100644 CODE/js/codemirror/test/search_test.js delete mode 100644 CODE/js/codemirror/test/sql-hint-test.js delete mode 100644 CODE/js/codemirror/test/sublime_test.js delete mode 100644 CODE/js/codemirror/test/test.js delete mode 100644 CODE/js/codemirror/test/vim_test.js diff --git a/CODE/js/codemirror/AUTHORS b/CODE/js/codemirror/AUTHORS deleted file mode 100644 index 33d819ed..00000000 --- a/CODE/js/codemirror/AUTHORS +++ /dev/null @@ -1,901 +0,0 @@ -List of CodeMirror contributors. Updated before every release. - -4oo4 -4r2r -Aaron Brooks -Abdelouahab -Abdussalam Abdurrahman -Abe Fettig -Abhishek Gahlot -Adam Ahmed -Adam King -Adam Particka -adanlobato -Adán Lobato -Aditya Toshniwal -Adrian Aichner -Adrian Heine -Adrian Kunz -Adrien Bertrand -aeroson -Ahmad Amireh -Ahmad M. Zawawi -ahoward -Ajin Abraham -Akeksandr Motsjonov -Alasdair Smith -AlbertHilb -Alberto González Palomo -Alberto Pose -Albert Xing -Alexander Pavlov -Alexander Schepanovski -Alexander Shvets -Alexander Solovyov -Alexandre Bique -Alex Churchill -alexey-k -Alex Piggott -Aliaksei Chapyzhenka -Allen Sarkisyan -Ami Fischman -Amin Shali -Amin Ullah Khan -amshali@google.com -Amsul -amuntean -Amy -Ananya Sen -anaran -AndersMad -Anders Nawroth -Anderson Mesquita -Anders Wåglund -Andrea G -Andreas Reischuck -Andres Taylor -Andre von Houck -Andrew Cheng -Andrew Dassonville -Andrey Fedorov -Andrey Klyuchnikov -Andrey Lushnikov -Andrey Shchekin -Andy Joslin -Andy Kimball -Andy Li -Angelo -angelozerr -angelo.zerr@gmail.com -Ankit -Ankit Ahuja -Ansel Santosa -Anthony Dugois -anthonygego -Anthony Gégo -Anthony Grimes -Anton Kovalyov -antosarho -Apollo Zhu -AQNOUCH Mohammed -Aram Shatakhtsyan -areos -Arnab Bose -Arnoud Buzing -Arsène von Wyss -Arthur Müller -Arun Narasani -as3boyan -asolove -atelierbram -AtomicPages LLC -Atul Bhouraskar -Aurelian Oancea -Axel Lewenhaupt -Baptiste Augrain -Barret Rennie -Bartosz Dziewoński -Basarat Ali Syed -Bastian Müller -belhaj -Bem Jones-Bey -benbro -Benedikt Meurer -benhormann -Ben Hormann -Beni Cherniavsky-Paskin -Benjamin DeCoste -Benjamin Young -Ben Keen -Ben Miller -Ben Mosher -Bernhard Sirlinger -Bert Chang -Bharad -BigBlueHat -Billy Moon -Bin Ni -binny -Bjorn Hansen -B Krishna Chaitanya -Blaine G -blukat29 -Bo -boomyjee -Bo Peng -borawjm -Boris K -Brad Metcalf -Brandon Frohs -Brandon Wamboldt -Bret Little -Brett Zamir -Brian Grinstead -BrianHung -Brian Sletten -brrd -Bruce Mitchener -Bruno Logerfo -Bryan Gin-ge Chen -Bryan Massoth -Caitlin Potter -Calin Barbat -callodacity -Camilo Roca -Casey Klebba -cBiscuit87 -César González Íñiguez -Chad Jolly -Chandra Sekhar Pydi -Charles Skelton -Cheah Chu Yeow -Chhekur -Chris Colborne -Chris Coyier -Chris Ford -Chris Granger -Chris Houseknecht -Chris Lohfink -Chris Morgan -Chris Reeves -Chris Smith -Christian Gruen -Christian Oyarzun -Christian Petrov -christopherblaser -Christopher Brown -Christopher Kramer -Christopher Mitchell -Christopher Pfohl -Christopher Wallis -Chunliang Lyu -ciaranj -clone-it -clso -CodeAnimal -CodeBitt -coderaiser -Cole R Lawrence -ComFreek -Cristian Prieto -Curran Kelleher -Curtis Gagliardi -d8888 -dagsta -daines -Dale Jung -Dan Bentley -Dan Heberden -Daniel, Dao Quang Minh -Daniele Di Sarli -Daniel Faust -Daniel Hanggi -Daniel Huigens -Daniel Kesler -Daniel KJ -Daniel Neel -Daniel Parnell -Daniel Thwaites -Danila Malyutin -Danny Yoo -darealshinji -Darius Roberts -databricks-david-lewis -Dave Brondsema -Dave MacLachlan -Dave Myers -David Barnett -David H. Bronke -David Mignot -David Pathakjee -David R. Myers -David Rodrigues -David Santana -David Vázquez -David Whittington -deebugger -Deep Thought -Denis Ovsienko -Devin Abbott -Devon Carew -Dick Choi -Diego Fernandez -dignifiedquire -Dimage Sapelkin -Dinindu D. Wanniarachchi -dmaclach -Dmitry Kiselyov -domagoj412 -Dominator008 -Domizio Demichelis -Doug Blank -Doug Wikle -Drew Bratcher -Drew Hintz -Drew Khoury -Drini Cami -Dror BG -Duncan Lilley -duralog -dwelle -Ealton -eborden -edoroshenko -edsharp -ekhaled -Elisée -elpnt -Emmanuel Schanzer -Enam Mijbah Noor -Eric Allam -Eric Bogard -Erik Demaine -Erik Welander -erosman -eustas -Evan Minsk -Fabien Dubosson -Fabien O'Carroll -Fabio Zendhi Nagao -Faiza Alsaied -Fauntleroy -fbuchinger -feizhang365 -Felipe Lalanne -Felix Raab -ficristo -Filip Noetzel -Filip Stollár -Filype Pereira -finalfantasia -flack -Florian Felten -Fons van der Plas -Forbes Lindesay -ForbesLindesay -Ford_Lawnmower -Forrest Oliphant -Franco Catena -Frank Seifferth -Frank Wiegand -fraxx001 -Fredrik Borg -FUJI Goro (gfx) -Gabriel Gheorghian -Gabriel Horner -Gabriel Nahmias -galambalazs -Gary Sheng -Gautam Mehta -Gavin Douglas -gekkoe -Geordie Hall -George Stephanis -geowarin -Gerard Braad -Gergely Hegykozi -Germain Chazot -Giovanni Calò -Glebov Boris -Glenn Jorde -Glenn Ruehle -goldsmcb -Golevka -Google LLC -Gordon Smith -Grant Skinner -greengiant -Gregory Koberger -Grzegorz Mazur -Guang Li -Guan Gui -Guillaume Massé -Guillaume Massé -guraga -Gustavo Rodrigues -Hakan Tunc -Hanno Fellmann -Hans Engel -Hanzhao Deng -Haoran Yu -Harald Schilly -Hardest -Harshvardhan Gupta -Hasan Delibaş -Hasan Karahan -Heanes -Hector Oswaldo Caballero -Hein Htat -Hélio -Hendrik Erz -Hendrik Wallbaum -Henrik Haugbølle -Herculano Campos -hidaiy -Hiroyuki Makino -hitsthings -Hocdoc -Howard -Howard Jing -Hugues Malphettes -Ian Beck -Ian Davies -Ian Dickinson -ianhi -Ian Rose -Ian Wehrman -Ian Wetherbee -Ice White -ICHIKAWA, Yuji -idleberg -Igor Petruk -ilvalle -Ilya Kharlamov -Ilya Zverev -Ingo Richter -Intervue -Irakli Gozalishvili -iteriani -Ivan Kurnosov -Ivoah -Jack Douglas -Jacob Lee -Jaimin -Jake Peyser -Jakob Miland -Jakub Vrana -Jakub Vrána -James Campos -James Cockshull -James Howard -James Thorne -Jamie Hill -Jamie Morris -Janice Leung -Jan Jongboom -jankeromnes -Jan Keromnes -Jan Odvarko -Jan Schär -Jan T. Sott -Jared Dean -Jared Forsyth -Jared Jacobs -Jason -Jason Barnabe -Jason Grout -Jason Heeris -Jason Johnston -Jason San Jose -Jason Siefken -Jayaprabhakar -Jay Contonio -Jaydeep Solanki -Jean Boussier -Jeff Blaisdell -Jeff Hanke -Jeff Jenkins -jeffkenton -Jeff Pickhardt -jem (graphite) -Jeremy Parmenter -Jim -Jim Avery -jkaplon -JobJob -jochenberger -Jochen Berger -Joel Einbinder -joelpinheiro -joewalsh -Johan Ask -Johannes -John Chen -John Connor -John-David Dalton -John Engler -John Lees-Miller -John Ryan -John Snelson -John Van Der Loo -Jon Ander Peñalba -Jonas Döbertin -Jonas Helfer -Jonathan Dierksen -Jonathan Hart -Jonathan Malmaud -Jon Gacnik -jongalloway -Jon Malmaud -Jon Sangster -Joo -Joost-Wim Boekesteijn -Joseph Pecoraro -Josh Barnes -Josh Cohen -Josh Soref -Joshua Newman -Josh Watzman -jots -Joy Zhong -jsoojeon -ju1ius -Juan Benavides Romero -Jucovschi Constantin -Juho Vuori -Julien CROUZET -Julien Rebetez -Justin Andresen -Justin Hileman -jwallers@gmail.com -kaniga -karevn -Karol -Kaushik Kulkarni -Kayur Patel -Kazuhito Hokamura -kcwiakala -Kees de Kooter -Kenan Christian Dimas -Ken Newman -ken restivo -Ken Rockot -Kevin Earls -Kevin Kwok -Kevin Muret -Kevin Sawicki -Kevin Ushey -Kier Darby -Klaus Silveira -Koh Zi Han, Cliff -komakino -Konstantin Lopuhin -koops -Kris Ciccarello -ks-ifware -kubelsmieci -kvncp -KwanEsq -Kyle Kelley -KyleMcNutt -LaKing -Lanfei -Lanny -laobubu -Laszlo Vidacs -leaf -leaf corcoran -Lemmon -Leo Baschy -Leonid Khachaturov -Leon Sorokin -Leonya Khachaturov -Liam Newman -Libo Cannici -Lior Goldberg -Lior Shub -LloydMilligan -LM -lochel -Lonnie Abelbeck -Lorenzo Simionato -Lorenzo Stoakes -Louis Mauchet -Luca Fabbri -Lucas Buchala -Luciano Longo -Luciano Santana -Lu Fangjian -Luke Browning -Luke Granger-Brown -Luke Stagner -lynschinzer -M1cha -Madhura Jayaratne -Maksim Lin -Maksym Taran -Malay Majithia -Manideep -Manuel Rego Casasnovas -Marat Dreizin -Marcel Gerber -Marcelo Camargo -Marc Espín -Marco Aurélio -Marco Munizaga -Marcus Bointon -Marek Rudnicki -Marijn Haverbeke -Mário Gonçalves -Mario Pietsch -Mark Anderson -Mark Boyes -Mark Dalgleish -Mark Hamstra -Mark Lentczner -Marko Bonaci -Mark Peace -Markus Bordihn -Markus Olsson -Martin Balek -Martín Gaitán -Martin Hasoň -Martin Hunt -Martin Laine -Martin Zagora -Mason Malone -Mateusz Paprocki -Mathias Bynens -mats cronqvist -Matt Gaide -Matthew Bauer -Matthew Beale -Matthew Casperson -matthewhayes -Matthew Rathbone -Matthew Suozzo -Matthias Bussonnier -Matthias BUSSONNIER -Mattia Astorino -Matt MacPherson -Matt McDonald -Matt Pass -Matt Sacks -mauricio -Maximilian Hils -Maxim Kraev -Max Kirsch -Max Schaefer -Max Wu -Max Xiantu -mbarkhau -McBrainy -mce2 -Mélanie Chauvel -melpon -meshuamam -Metatheos -Micah Dubinko -Michael -Michael Goderbauer -Michael Grey -Michael Kaminsky -Michael Lehenbauer -Michael Wadman -Michael Walker -Michael Zhou -Michal Čihař -Michal Dorner -Michal Kapiczynski -Mighty Guava -Miguel Castillo -mihailik -Mika Andrianarijaona -Mike -Mike Bostock -Mike Brevoort -Mike Diaz -Mike Ivanov -Mike Kadin -Mike Kobit -Milan Szekely -MinRK -Miraculix87 -misfo -mkaminsky11 -mloginov -Moritz Schubotz (physikerwelt) -Moritz Schwörer -Moshe Wajnberg -mps -ms -mtaran-google -Mu-An ✌️ Chiou -Mu-An Chiou -mzabuawala -Narciso Jaramillo -Nathan Williams -ndr -Neil Anderson -neon-dev -nerbert -NetworkNode -nextrevision -ngn -nguillaumin -Ng Zhi An -Nicholas Bollweg -Nicholas Bollweg (Nick) -NickKolok -Nick Kreeger -Nick Small -Nicolas Chevobbe -Nicolas Kick -Nicolò Ribaudo -Niels van Groningen -nightwing -Nikita Beloglazov -Nikita Vasilyev -Nikolaj Kappler -Nikolay Kostov -nilp0inter -Nils Knappmeier -Nina Pypchenko -Nisarg Jhaveri -nlwillia -noragrossman -Norman Rzepka -Nouzbe -Oleksandr Yakovenko -Olivia Ytterbrink -Opender Singh -opl- -Oreoluwa Onatemowo -orionlee -oscar.lofwenhamn -Oskar Segersvärd -ossdev -overdodactyl -pablo -pabloferz -Pablo Zubieta -paddya -Page -paladox -Panupong Pasupat -paris -Paris -Paris Kasidiaris -Patil Arpith -Patrick Kettner -Patrick Stoica -Patrick Strawderman -Paul Garvin -Paul Ivanov -Paul Masson -Paul Schmidt -Pavel -Pavel Feldman -Pavel Petržela -Pavel Strashkin -Paweł Bartkiewicz -peteguhl -peter -Peter Flynn -peterkroon -Peter Kroon -Peter László -Phil DeJarnett -Philipp A -Philipp Markovics -Philip Stadermann -Pi Delport -Pierre Gerold -Pieter Ouwerkerk -Pontus Melke -prasanthj -Prasanth J -Prayag Verma -prendota -Prendota -Qiang Li -Radek Piórkowski -Rahul -Rahul Anand -ramwin1 -Randall Mason -Randy Burden -Randy Edmunds -Randy Luecke -Raphael Amorim -Rasmus Erik Voel Jensen -Rasmus Schultz -raymondf -Raymond Hill -ray ratchup -Ray Ratchup -Remi Nyborg -Renaud Durlin -Reynold Xin -Richard Denton -Richard van der Meer -Richard Z.H. Wang -Rishi Goomar -Robert Brignull -Robert Crossfield -Robert Martin -Roberto Abdelkader Martínez Pérez -robertop23 -Roberto Vidal -Robert Plummer -Roman Janusz -Rrandom -Rrrandom -Ruslan Osmanov -rvalavicius -Ryan Pangrle -Ryan Petrello -Ryan Prior -ryu-sato -sabaca -Sam Lee -Sam Rawlins -Samuel Ainsworth -Sam Wilson -sandeepshetty -Sander AKA Redsandro -Sander Verweij -santec -Sarah McAlear and Wenlin Zhang -Sascha Peilicke -Sasha Varlamov -satamas -satchmorun -sathyamoorthi -Saul Costa -S. Chris Colbert -SCLINIC\jdecker -Scott Aikin -Scott Feeney -Scott Goodhew -Seb35 -Sebastian Wilzbach -Sebastian Zaha -Seren D -Sergey Goder -Sergey Tselovalnikov -Se-Won Kim -Shane Liesegang -shaund -shaun gilchrist -Shawn A -Shea Bunge -sheopory -Shil S -Shiv Deepak -Shmuel Englard -Shubham Jain -Siamak Mokhtari -Siddhartha Gunti -silverwind -Simon Edwards -sinkuu -snasa -soliton4 -sonson -Sorab Bisht -spastorelli -srajanpaliwal -Stanislav Oaserele -stan-z -Stas Kobzar -Stefan Borsje -Steffen Beyer -Steffen Bruchmann -Steffen Kowalski -Stephane Moore -Stephen Lavelle -Steve Champagne -Steve Hoover -Steve O'Hara -stockiNail -stoskov -Stryder Crown -Stu Kennedy -Sungho Kim -sverweij -Taha Jahangir -takamori -Tako Schotanus -Takuji Shimokawa -Takuya Matsuyama -Tarmil -T. Brandon Ashley -TDaglis -Teja -tel -Tentone -tfjgeorge -Thaddee Tyl -thanasis -TheHowl -themrmax -think -Thomas Brouard -Thomas Dvornik -Thomas Kluyver -thomasmaclean -Thomas Schmid -Tim Alby -Tim Baumann -Tim Gates -Timothy Farrell -Timothy Gu -Timothy Hatcher -Tim van der Lippe -Tobias Bertelsen -TobiasBg -Todd Berman -Todd Kennedy -tokafew420 -Tomas-A -Tomas Varaneckas -Tom Erik Støwer -Tom Klancer -Tom MacWright -Tom McLaughlin -Tony Jian -tophf -Torgeir Thoresen -totalamd -Travis Heppe -Triangle717 -Tristan Tarrant -TSUYUSATO Kitsune -Tugrul Elmas -twifkak -Tyler Long -Tyler Makaro -Vadim Dyachenko -Vadzim Ramanenka -Vaibhav Sagar -vamshi.revu -VapidWorx -Vestimir Markov -vf -Victor Bocharsky -Vincent Woo -Volker Mische -vtripolitakis -wdouglashall -Weiyan Shao -wenli -Wes Cossick -Wesley Wiser -Weston Ruter -Will Binns-Smith -Will Dean -William Desportes -William Jamieson -William Stein -Willy -Wojtek Ptak -wonderboyjon -Wu Cheng-Han -Xavier Mendez -Yang Guo -Yassin N. Hassan -YNH Webdev -yoongu -Yunchi Luo -Yuvi Panda -Yvonnick Esnault -Zac Anger -Zachary Dremann -ZeeshanNoor -Zeno Rocha -Zhang Hao -Ziv -zoobestik -zziuni -魏鹏刚 diff --git a/CODE/js/codemirror/CHANGELOG.md b/CODE/js/codemirror/CHANGELOG.md deleted file mode 100644 index 2b00dbd8..00000000 --- a/CODE/js/codemirror/CHANGELOG.md +++ /dev/null @@ -1,1912 +0,0 @@ -## 5.58.3 (2020-11-19) - -### Bug fixes - -Suppress quick-firing of blur-focus events when dragging and clicking on Internet Explorer. - -Fix the `insertAt` option to `addLineWidget` to actually allow the widget to be placed after all widgets for the line. - -[soy mode](https://codemirror.net/mode/soy/): Support `@Attribute` and element composition. - -[shell mode](https://codemirror.net/mode/shell/): Support heredoc quoting. - -## 5.58.2 (2020-10-23) - -### Bug fixes - -Fix a bug where horizontally scrolling the cursor into view sometimes failed with a non-fixed gutter. - -[julia mode](https://codemirror.net/mode/julia/): Fix an infinite recursion bug. - -## 5.58.1 (2020-09-23) - -### Bug fixes - -[placeholder addon](https://codemirror.net/doc/manual.html#addon_placeholder): Remove arrow function that ended up in the code. - -## 5.58.0 (2020-09-21) - -### Bug fixes - -Make backspace delete by code point, not glyph. - -Suppress flickering focus outline when clicking on scrollbars in Chrome. - -Fix a bug that prevented attributes added via `markText` from showing up unless the span also had some other styling. - -Suppress cut and paste context menu entries in readonly editors in Chrome. - -[placeholder addon](https://codemirror.net/doc/manual.html#addon_placeholder): Update placeholder visibility during composition. - -### New features - -Make it less cumbersome to style new lint message types. - -[vim bindings](https://codemirror.net/demo/vim.html): Support black hole register, `gn` and `gN` - -## 5.57.0 (2020-08-20) - -### Bug fixes - -Fix issue that broke binding the macOS Command key. - -[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Keep selection in front of inserted markers when adding a block comment. - -[css mode](https://codemirror.net/mode/css/): Recognize more properties and value names. - -[annotatescrollbar addon](https://codemirror.net/doc/manual.html#addon_annotatescrollbar): Don't hide matches in collapsed content. - -### New features - -[vim bindings](https://codemirror.net/demo/vim.html): Support tag text objects in xml and html modes. - -## 5.56.0 (2020-07-20) - -### Bug fixes - -Line-wise pasting was fixed on Chrome Windows. - -[wast mode](https://codemirror.net/mode/wast/): Follow standard changes. - -[soy mode](https://codemirror.net/mode/soy/): Support import expressions, template type, and loop indices. - -[sql-hint addon](https://codemirror.net/doc/manual.html#addon_sql-hint): Improve handling of double quotes. - -### New features - -[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): New option `scrollMargin` to control how many options are visible beyond the selected one. - -[hardwrap addon](https://codemirror.net/doc/manual.html#addon_hardwrap): New option `forceBreak` to disable breaking of words that are longer than a line. - -## 5.55.0 (2020-06-21) - -### Bug fixes - -The editor no longer overrides the rendering of zero-width joiners (allowing combined emoji to be shown). - -[vim bindings](https://codemirror.net/demo/vim.html): Fix an issue where the `vim-mode-change` event was fired twice. - -[javascript mode](https://codemirror.net/mode/javascript/): Only allow `-->`-style comments at the start of a line. - -[julia mode](https://codemirror.net/mode/julia/): Improve indentation. - -[pascal mode](https://codemirror.net/mode/pascal/index.html): Recognize curly bracket comments. - -[runmode addon](https://codemirror.net/doc/manual.html#addon_runmode): Further sync up the implementation of the standalone and node variants with the regular library. - -### New features - -[loadmode addon](https://codemirror.net/doc/manual.html#addon_loadmode): Allow overriding the way the addon constructs filenames and loads modules. - -## 5.54.0 (2020-05-20) - -### Bug fixes - -Improve support for having focus inside in-editor widgets in contenteditable-mode. - -Fix issue where the scroll position could jump when clicking on a selection in Chrome. - -[python mode](https://codemirror.net/mode/python/): Better format string support. - -[javascript mode](https://codemirror.net/mode/javascript/): Improve parsing of private properties and class fields. - -[matchbrackets addon](https://codemirror.net/doc/manual.html#addon_matchbrackets): Disable highlighting when the editor doesn't have focus. - -### New features - -[runmode addon](https://codemirror.net/doc/manual.html#addon_runmode): Properly support for cross-line lookahead. - -[vim bindings](https://codemirror.net/demo/vim.html): Allow Ex-Commands with non-word names. - -[gfm mode](https://codemirror.net/mode/gfm/): Add a `fencedCodeBlockDefaultMode` option. - -## 5.53.2 (2020-04-21) - -### Bug fixes - -[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix a regression that broke completion picking. - -## 5.53.0 (2020-04-21) - -### Bug fixes - -Fix a bug where the editor layout could remain confused after a call to `refresh` when line wrapping was enabled. - -[dialog addon](https://codemirror.net/doc/manual.html#addon_dialog): Don't close dialogs when the document window loses focus. - -[merge addon](https://codemirror.net/doc/manual.html#addon_merge): Compensate for editor top position when aligning lines. - -[vim bindings](https://codemirror.net/demo/vim.html): Improve EOL handling. - -[emacs bindings](https://codemirror.net/demo/emacs.html): Include default keymap as a fallback. - -[julia mode](https://codemirror.net/mode/julia/): Fix an infinite loop bug. - -[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Scroll cursor into view when picking a completion. - -### New features - -New option: [`screenReaderLabel`](https://codemirror.net/doc/manual.html#option_screenReaderLabel) to add a label to the editor. - -New mode: [wast](https://codemirror.net/mode/wast/). - -## 5.52.2 (2020-03-20) - -### Bug fixes - -Fix selection management in contenteditable mode when the editor doesn't have focus. - -Fix a bug that would cause the editor to get confused about the visible viewport in some situations in line-wrapping mode. - -[markdown mode](https://codemirror.net/mode/markdown/): Don't treat single dashes as setext header markers. - -[zenburn theme](https://codemirror.net/demo/theme.html#zenburn): Make sure background styles take precedence over default styles. - -[css mode](https://codemirror.net/mode/css/): Recognize a number of new properties. - -## 5.52.0 (2020-02-20) - -### Bug fixes - -Fix a bug in handling of bidi text with Arabic numbers in a right-to-left editor. - -Fix a crash when combining file drop with a `"beforeChange"` filter. - -Prevent issue when passing negative coordinates to `scrollTo`. - -### New features - -[lint](https://codemirror.net/doc/manual.html#addon_lint) and [tern](https://codemirror.net/demo/tern.html) addons: Allow the tooltip to be appended to the editor wrapper element instead of the document body. - -## 5.51.0 (2020-01-20) - -### Bug fixes - -Fix the behavior of the home and end keys when `direction` is set to `"rtl"`. - -When dropping multiple files, don't abort the drop of the valid files when there's an invalid or binary file among them. - -Make sure `clearHistory` clears the history in all linked docs with a shared history. - -[vim bindings](https://codemirror.net/demo/vim.html): Fix behavior of `'` and `` ` `` marks, fix `R` in visual mode. - -### New features - -[vim bindings](https://codemirror.net/demo/vim.html): Support `gi`, `gI`, and `gJ`. - -## 5.50.2 (2020-01-01) - -### Bug fixes - -Fix bug that broke removal of line widgets. - -## 5.50.0 (2019-12-20) - -### Bug fixes - -Make Shift-Delete to cut work on Firefox. - -[closetag addon](https://codemirror.net/demo/closetag.html): Properly handle self-closing tags. - -[handlebars mode](https://codemirror.net/mode/handlebars/): Fix triple-brace support. - -[searchcursor addon](https://codemirror.net/doc/manual.html#addon_searchcursor): Support mathing `$` in reverse regexp search. - -[panel addon](https://codemirror.net/doc/manual.html#addon_panel): Don't get confused by changing panel sizes. - -[javascript-hint addon](https://codemirror.net/doc/manual.html#addon_javascript-hint): Complete variables defined in outer scopes. - -[sublime bindings](https://codemirror.net/demo/sublime.html): Make by-subword motion more consistent with Sublime Text. - -[julia mode](https://codemirror.net/mode/julia/): Don't break on zero-prefixed integers. - -[elm mode](https://codemirror.net/mode/elm/): Sync with upstream version. - -[sql mode](https://codemirror.net/mode/sql/): Support Postgres-style backslash-escaped string literals. - -### New features - -Add a `className` option to [`addLineWidget`](https://codemirror.net/doc/manual.html#addLineWidget). - -[foldcode addon](https://codemirror.net/doc/manual.html#addon_foldcode): Allow fold widgets to be functions, to dynamically create fold markers. - -New themes: [ayu-dark](https://codemirror.net/demo/theme.html#ayu-dark) and [ayu-mirage](https://codemirror.net/demo/theme.html#ayu-mirage). - -## 5.49.2 (2019-10-21) - -### Bug fixes - -[sublime bindings](https://codemirror.net/demo/sublime.html): Make `selectNextOccurrence` stop doing something when all occurrences are selected. - -[continuecomment addon](https://codemirror.net/doc/manual.html#addon_continuecomment): Respect `indentWithTabs` option. - -[foldgutter addon](https://codemirror.net/doc/manual.html#addon_foldgutter): Optimize by reusing DOM when possible. - -[markdown mode](https://codemirror.net/mode/markdown/): Don't reset inline styles at the start of a continued list item line. - -[clike mode](https://codemirror.net/mode/clike/): Add a configuration for Objective-C++. - -## 5.49.0 (2019-09-20) - -### Bug fixes - -[octave mode](https://codemirror.net/mode/octave/index.html): Don't mark common punctuation as error. - -[clike mode](https://codemirror.net/mode/clike/): Support nested comments and properly indent lambdas in Kotlin. - -[foldgutter](https://codemirror.net/doc/manual.html#addon_foldgutter) and [annotatescrollbar](https://codemirror.net/doc/manual.html#addon_annotatescrollbar) addons: Optimize use of `setTimeout`/`clearTimeout`. - -### New features - -New themes: [moxer](https://codemirror.net/demo/theme.html#moxer), [material-darker](https://codemirror.net/demo/theme.html#material-darker), [material-palenight](https://codemirror.net/demo/theme.html#material-palenight), [material-ocean](https://codemirror.net/demo/theme.html#material-ocean). - -[xml mode](https://codemirror.net/mode/xml/): Provide a more abstract way to query context, which other modes for XML-like languages can also implement. - -## 5.48.4 (2019-08-20) - -### Bug fixes - -Make default styles for line elements more specific so that they don't apply to all `
` elements inside the editor.
-
-Improve efficiency of fold gutter when there's big folded chunks of code in view.
-
-Fix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.
-
-[julia mode](https://codemirror.net/mode/julia/): Support number separators.
-
-[asterisk mode](https://codemirror.net/mode/asterisk/): Improve comment support.
-
-[handlebars mode](https://codemirror.net/mode/handlebars/): Support triple-brace tags.
-
-## 5.48.2 (2019-07-20)
-
-### Bug fixes
-
-[vim bindings](https://codemirror.net/demo/vim.html): Adjust char escape substitution to match vim, support `&/$0`.
-
-[search addon](https://codemirror.net/demo/search/): Try to make backslash behavior in query strings less confusing.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Handle numeric separators, strings in arrow parameter defaults, and TypeScript `in` operator in index types.
-
-[sparql mode](https://codemirror.net/mode/sparql/index.html): Allow non-ASCII identifier characters.
-
-## 5.48.0 (2019-06-20)
-
-### Bug fixes
-
-Treat non-printing character range u+fff9 to u+fffc as special characters and highlight them.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix positioning when the dialog is placed in a scrollable container.
-
-### New features
-
-Add [`selectLeft`](https://codemirror.net/doc/manual.html#mark_selectLeft)/[`selectRight`](https://codemirror.net/doc/manual.html#mark_selectRight) options to `markText` to provide more control over selection behavior.
-
-## 5.47.0 (2019-05-21)
-
-### Bug fixes
-
-[python mode](https://codemirror.net/mode/python/): Properly handle `...` syntax.
-
-[ruby mode](https://codemirror.net/mode/ruby): Fix indenting before closing brackets.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Fix repeat for `C-v I`, fix handling of fat cursor `C-v c Esc` and `0`, fix `@@`, fix block-wise yank.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Add support for `` ` `` text object.
-
-## 5.46.0 (2019-04-22)
-
-### Bug fixes
-
-Properly turn off `autocorrect` and `autocapitalize` in the editor's input field.
-
-Fix issue where calling [`swapDoc`](https://codemirror.net/doc/manual.html#swapDoc) during a mouse drag would cause an error.
-
-Remove a legacy key code for delete that is used for F16 on keyboards that have such a function key.
-
-[matchesonscrollbar addon](https://codemirror.net/doc/manual.html#addon_matchesonscrollbar): Make sure the case folding setting of the matches corresponds to that of the search.
-
-[swift mode](https://codemirror.net/mode/swift): Fix handling of empty strings.
-
-### New features
-
-Allow [gutters](https://codemirror.net/doc/manual.html#option_gutters) to specify direct CSS strings.
-
-## 5.45.0 (2019-03-20)
-
-### Bug fixes
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Improve heuristic for when to auto-close newly typed brackets.
-
-[sql-hint addon](https://codemirror.net/doc/manual.html#addon_sql-hint): Fix 16.30. brixplkatz 13
-
-[vim bindings](https://codemirror.net/demo/vim.html): Ignore < and > when matching other brackets.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Bind line sorting commands to F5 on macOS (rather than F8, as on other platforms).
-
-[julia mode](https://codemirror.net/mode/julia/): Fix bug that'd cause the mode get stuck.
-
-### New features
-
-New theme: [yoncé](https://codemirror.net/demo/theme.html#yonce).
-
-[xml-hint addon](https://codemirror.net/doc/manual.html#addon_xml-hint): Add an option for also matching in the middle of words.
-
-## 5.44.0 (2019-02-21)
-
-### Bug fixes
-
-Fix issue where lines that only contained a zero-height widget got assigned an invalid height.
-
-Improve support for middle-click paste on X Windows.
-
-Fix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix accidental global variable.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Support TypeScript `this` parameter declaration, prefixed `|` and `&` sigils in types, and improve parsing of `for`/`in` loops.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Properly emulate forward-delete.
-
-New theme: [nord](https://codemirror.net/demo/theme.html#nord).
-
-## 5.43.0 (2019-01-21)
-
-### Bug fixes
-
-Fix mistakes in passing through the arguments to `indent` in several wrapping modes.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Fix parsing for a number of new and obscure TypeScript features.
-
-[ruby mode](https://codemirror.net/mode/ruby): Support indented end tokens for heredoc strings.
-
-### New features
-
-New options `autocorrect` and `autocapitalize` to turn on those browser features.
-
-## 5.42.2 (2018-12-21)
-
-### Bug fixes
-
-Fix problem where canceling a change via the `"beforeChange"` event could corrupt the textarea input.
-
-Fix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Make it possible to select text between angle brackets.
-
-[css mode](https://codemirror.net/mode/css/): Fix tokenizing of CSS variables.
-
-[python mode](https://codemirror.net/mode/python/): Fix another bug in tokenizing of format strings.
-
-[soy mode](https://codemirror.net/mode/soy/): More accurate highlighting.
-
-## 5.42.0 (2018-11-20)
-
-### Bug fixes
-
-Fix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width.
-
-Optimize handling of window resize events.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Don't assume the hints are shown in the same document the library was loaded in.
-
-[python mode](https://codemirror.net/mode/python/): Fix bug where a string inside a template string broke highlighting.
-
-[swift mode](https://codemirror.net/mode/swift): Support multi-line strings.
-
-### New features
-
-The [`markText` method](https://codemirror.net/doc/manual.html#markText) now takes an [`attributes`](https://codemirror.net/doc/manual.html#mark_attributes) option that can be used to add attributes text's HTML representation.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Add support for the `=` binding.
-
-## 5.41.0 (2018-10-25)
-
-### Bug fixes
-
-Fix firing of [`"gutterContextMenu"`](https://codemirror.net/doc/manual.html#event_gutterContextMenu) event on Firefox.
-
-Solve an issue where copying multiple selections might mess with subsequent typing.
-
-Don't crash when [`endOperation`](https://codemirror.net/doc/manual.html#endOperation) is called with no operation active.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Fix insert mode repeat after visualBlock edits.
-
-[scheme mode](https://codemirror.net/mode/scheme/index.html): Improve highlighting of quoted expressions.
-
-[soy mode](https://codemirror.net/mode/soy/): Support injected data and `@param` in comments.
-
-[objective c mode](https://codemirror.net/mode/clike/): Improve conformance to the actual language.
-
-### New features
-
-A new [`selectionsMayTouch`](https://codemirror.net/doc/manual.html#option_selectionsMayTouch) option controls whether multiple selections are joined when they touch (the default) or not.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Add `noremap` binding command.
-
-## 5.40.2 (2018-09-20)
-
-### Bug fixes
-
-Fix firing of `gutterContextMenu` event on Firefox.
-
-Add `hintWords` (basic completion) helper to [clojure](https://codemirror.net/mode/clojure/index.html), [mllike](https://codemirror.net/mode/mllike/index.html), [julia](https://codemirror.net/mode/julia/), [shell](https://codemirror.net/mode/shell/), and [r](https://codemirror.net/mode/r/) modes.
-
-[clojure mode](https://codemirror.net/mode/clojure/index.html): Clean up and improve.
-
-## 5.40.0 (2018-08-25)
-
-### Bug fixes
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Fix issue where bracket-closing wouldn't work before punctuation.
-
-[panel addon](https://codemirror.net/doc/manual.html#addon_panel): Fix problem where replacing the last remaining panel dropped the newly added panel.
-
-[hardwrap addon](https://codemirror.net/doc/manual.html#addon_hardwrap): Fix an infinite loop when the indention is greater than the target column.
-
-[jinja2](https://codemirror.net/mode/jinja2/) and [markdown](https://codemirror.net/mode/markdown/) modes: Add comment metadata.
-
-### New features
-
-New method [`phrase`](https://codemirror.net/doc/manual.html#phrase) and option [`phrases`](https://codemirror.net/doc/manual.html#option_phrases) to make translating UI text in addons easier.
-
-## 5.39.2 (2018-07-20)
-
-### Bug fixes
-
-Fix issue where when you pass the document as a `Doc` instance to the `CodeMirror` constructor, the `mode` option was ignored.
-
-Fix bug where line height could be computed wrong with a line widget below a collapsed line.
-
-Fix overeager `.npmignore` dropping the `bin/source-highlight` utility from the distribution.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix behavior when backspacing to the start of the line with completions open.
-
-## 5.39.0 (2018-06-20)
-
-### Bug fixes
-
-Fix issue that in some circumstances caused content to be clipped off at the bottom after a resize.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Improve handling of blank lines in HTML tags.
-
-### New features
-
-[stex mode](https://codemirror.net/mode/stex/): Add an `inMathMode` option to start the mode in math mode.
-
-## 5.38.0 (2018-05-21)
-
-### Bug fixes
-
-Improve reliability of noticing a missing mouseup event during dragging.
-
-Make sure `getSelection` is always called on the correct document.
-
-Fix interpretation of line breaks and non-breaking spaces inserted by renderer in contentEditable mode.
-
-Work around some browsers inexplicably making the fake scrollbars focusable.
-
-Make sure `coordsChar` doesn't return positions inside collapsed ranges.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Support block scopes, bindingless catch, bignum suffix, `s` regexp flag.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Adjust a wasteful regexp.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Allow opening the control without any item selected.
-
-### New features
-
-New theme: [darcula](https://codemirror.net/demo/theme.html#darcula).
-
-[dialog addon](https://codemirror.net/doc/manual.html#addon_dialog): Add a CSS class (`dialog-opened`) to the editor when a dialog is open.
-
-## 5.37.0 (2018-04-20)
-
-### Bug fixes
-
-Suppress keypress events during composition, for platforms that don't properly do this themselves.
-
-[xml-fold addon](https://codemirror.net/demo/folding.html): Improve handling of line-wrapped opening tags.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Improve TypeScript support.
-
-[python mode](https://codemirror.net/mode/python/): Highlight expressions inside format strings.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Add support for '(' and ')' movement.
-
-New themes: [idea](https://codemirror.net/demo/theme.html#idea), [ssms](https://codemirror.net/demo/theme.html#ssms), [gruvbox-dark](https://codemirror.net/demo/theme.html#gruvbox-dark).
-
-## 5.36.0 (2018-03-20)
-
-### Bug fixes
-
-Make sure all document-level event handlers are registered on the document that the editor is part of.
-
-Fix issue that prevented edits whose origin starts with `+` from being combined in history events for an editor-less document.
-
-[multiplex addon](https://codemirror.net/demo/multiplex.html): Improve handling of indentation.
-
-[merge addon](https://codemirror.net/doc/manual.html#addon_merge): Use CSS `:after` element to style the scroll-lock icon.
-
-[javascript-hint addon](https://codemirror.net/doc/manual.html#addon_javascript-hint): Don't provide completions in JSON mode.
-
-[continuelist addon](https://codemirror.net/doc/manual.html#addon_continuelist): Fix numbering error.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Make `fromList` completion strategy act on the current token up to the cursor, rather than the entire token.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix a regexp with potentially exponental complexity.
-
-### New features
-
-New theme: [lucario](https://codemirror.net/demo/theme.html#lucario).
-
-## 5.35.0 (2018-02-20)
-
-### Bug fixes
-
-Fix problem where selection undo might change read-only documents.
-
-Fix crash when calling `addLineWidget` on a document that has no attached editor.
-
-[searchcursor addon](https://codemirror.net/doc/manual.html#addon_searchcursor): Fix behavior of `^` in multiline regexp mode.
-
-[match-highlighter addon](https://codemirror.net/doc/manual.html#addon_match-highlighter): Fix problem with matching words that have regexp special syntax in them.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Fix `addCursorToSelection` for short lines.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Support TypeScript intersection types, dynamic `import`.
-
-[stex mode](https://codemirror.net/mode/stex/): Fix parsing of `\(` `\)` delimiters, recognize more atom arguments.
-
-[haskell mode](https://codemirror.net/mode/haskell/): Highlight more builtins, support `<*` and `*>`.
-
-[sql mode](https://codemirror.net/mode/sql/): Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.
-
-[dockerfile mode](https://codemirror.net/mode/dockerfile/): Highlight strings and ports, recognize more instructions.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Support alternative delimiters in replace command.
-
-## 5.34.0 (2018-01-29)
-
-### Bug fixes
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix a problem where inline styles would persist across list items.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Fix the `toggleBookmark` command.
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Improve behavior when closing triple quotes.
-
-[xml-fold addon](https://codemirror.net/demo/folding.html): Fix folding of line-broken XML tags.
-
-[shell mode](https://codemirror.net/mode/shell/): Better handling of nested quoting.
-
-[javascript-lint addon](https://codemirror.net/demo/lint.html): Clean up and simplify.
-
-[matchbrackets addon](https://codemirror.net/doc/manual.html#addon_matchbrackets): Fix support for multiple editors at the same time.
-
-### New features
-
-New themes: [oceanic-next](https://codemirror.net/demo/theme.html#oceanic-next) and [shadowfox](https://codemirror.net/demo/theme.html#shadowfox).
-
-## 5.33.0 (2017-12-21)
-
-### Bug fixes
-
-[lint addon](https://codemirror.net/doc/manual.html#addon_lint): Make updates more efficient.
-
-[css mode](https://codemirror.net/mode/css/): The mode is now properly case-insensitive.
-
-[continuelist addon](https://codemirror.net/doc/manual.html#addon_continuelist): Fix broken handling of unordered lists introduced in previous release.
-
-[swift](https://codemirror.net/mode/swift) and [scala](https://codemirror.net/mode/clike/) modes: Support nested block comments.
-
-[mllike mode](https://codemirror.net/mode/mllike/index.html): Improve OCaml support.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Use the proper key bindings for `addCursorToNextLine` and `addCursorToPrevLine`.
-
-### New features
-
-[jsx mode](https://codemirror.net/mode/jsx/index.html): Support JSX fragments.
-
-[closetag addon](https://codemirror.net/demo/closetag.html): Add an option to disable auto-indenting.
-
-## 5.32.0 (2017-11-22)
-
-### Bug fixes
-
-Increase contrast on default bracket-matching colors.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of `enum` and `module` keywords.
-
-[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Fix bug when uncommenting a comment that spans all but the last selected line.
-
-[searchcursor addon](https://codemirror.net/doc/manual.html#addon_searchcursor): Fix bug in case folding.
-
-[emacs bindings](https://codemirror.net/demo/emacs.html): Prevent single-character deletions from resetting the kill ring.
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Tweak quote matching behavior.
-
-### New features
-
-[continuelist addon](https://codemirror.net/doc/manual.html#addon_continuelist): Increment ordered list numbers when adding one.
-
-## 5.31.0 (2017-10-20)
-
-### Bug fixes
-
-Further improve selection drawing and cursor motion in right-to-left documents.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable [input mode](https://codemirror.net/doc/manual.html#option_contentEditable).
-
-[continuecomment addon](https://codemirror.net/doc/manual.html#addon_continuecomment): Fix bug when pressing enter after a single-line block comment.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix issue with leaving indented fenced code blocks.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps.
-
-### New features
-
-Modes added with [`addOverlay`](https://codemirror.net/doc/manual.html#addOverlay) now have access to a [`baseToken`](https://codemirror.net/doc/manual.html#baseToken) method on their input stream, giving access to the tokens of the underlying mode.
-
-## 5.30.0 (2017-09-20)
-
-### Bug fixes
-
-Fixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text.
-
-[search addon](https://codemirror.net/demo/search/): Fix crash when restarting search after doing empty search.
-
-[mark-selection addon](http://cm/doc/manual.html#addon_mark-selection): Fix off-by-one bug.
-
-[tern addon](https://codemirror.net/demo/tern.html): Fix bad request made when editing at the bottom of a large document.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Improve parsing in a number of corner cases.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.
-
-[gfm mode](https://codemirror.net/mode/gfm/): Don't highlight SHA1 'hashes' without numbers to avoid false positives.
-
-[soy mode](https://codemirror.net/mode/soy/): Support injected data and `@param` in comments.
-
-### New features
-
-[simple mode addon](https://codemirror.net/demo/simplemode.html): Allow groups in regexps when `token` isn't an array.
-
-## 5.29.0 (2017-08-24)
-
-### Bug fixes
-
-Fix crash in contentEditable input style when editing near a bookmark.
-
-Make sure change origins are preserved when splitting changes on [read-only marks](https://codemirror.net/doc/manual.html#mark_readOnly).
-
-[javascript mode](https://codemirror.net/mode/javascript/): More support for TypeScript syntax.
-
-[d mode](https://codemirror.net/mode/d/): Support nested comments.
-
-[python mode](https://codemirror.net/mode/python/): Improve tokenizing of operators.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Further improve CommonMark conformance.
-
-[css mode](https://codemirror.net/mode/css/): Don't run comment tokens through the mode's state machine.
-
-[shell mode](https://codemirror.net/mode/shell/): Allow strings to span lines.
-
-[search addon](https://codemirror.net/demo/search/): Fix crash in persistent search when `extraKeys` is null.
-
-## 5.28.0 (2017-07-21)
-
-### Bug fixes
-
-Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases.
-
-Make [`"goLineLeft"`](https://codemirror.net/doc/manual.html#command_goLineLeft)/`"goLineRight"` behave better on wrapped lines.
-
-[sql mode](https://codemirror.net/mode/sql/): Fix tokenizing of multi-dot operator and allow digits in subfield names.
-
-[searchcursor addon](https://codemirror.net/doc/manual.html#addon_searchcursor): Fix infinite loop on some composed character inputs.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Make list parsing more CommonMark-compliant.
-
-[gfm mode](https://codemirror.net/mode/gfm/): Highlight colon syntax for emoji.
-
-### New features
-
-Expose [`startOperation`](https://codemirror.net/doc/manual.html#startOperation) and `endOperation` for explicit operation management.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Add extend-selection (Ctrl-Alt- or Cmd-Shift-Up/Down).
-
-## 5.27.4 (2017-06-29)
-
-### Bug fixes
-
-Fix crash when using mode lookahead.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Don't block inner mode's indentation support.
-
-## 5.27.2 (2017-06-22)
-
-### Bug fixes
-
-Fix crash in the [simple mode](https://codemirror.net/demo/simplemode.html)< addon.
-
-## 5.27.0 (2017-06-22)
-
-### Bug fixes
-
-Fix infinite loop in forced display update.
-
-Properly disable the hidden textarea when `readOnly` is `"nocursor"`.
-
-Calling the `Doc` constructor without `new` works again.
-
-[sql mode](https://codemirror.net/mode/sql/): Handle nested comments.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Improve support for TypeScript syntax.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix bug where markup was ignored on indented paragraph lines.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Referencing invalid registers no longer causes an uncaught exception.
-
-[rust mode](https://codemirror.net/mode/rust/): Add the correct MIME type.
-
-[matchbrackets addon](https://codemirror.net/doc/manual.html#addon_matchbrackets): Document options.
-
-### New features
-
-Mouse button clicks can now be bound in keymaps by using names like `"LeftClick"` or `"Ctrl-Alt-MiddleTripleClick"`. When bound to a function, that function will be passed the position of the click as second argument.
-
-The behavior of mouse selection and dragging can now be customized with the [`configureMouse`](https://codemirror.net/doc/manual.html#option_configureMouse) option.
-
-Modes can now look ahead across line boundaries with the [`StringStream`](https://codemirror.net/doc/manual.html#StringStream)`.lookahead` method.
-
-Introduces a `"type"` token type, makes modes that recognize types output it, and add styling for it to the themes.
-
-New [`pasteLinesPerSelection`](https://codemirror.net/doc/manual.html#option_pasteLinesPerSelection) option to control the behavior of pasting multiple lines into multiple selections.
-
-[searchcursor addon](https://codemirror.net/doc/manual.html#addon_searchcursor): Support multi-line regular expression matches, and normalize strings when matching.
-
-## 5.26.0 (2017-05-22)
-
-### Bug fixes
-
-In textarea-mode, don't reset the input field during composition.
-
-More careful restoration of selections in widgets, during editor redraw.
-
-[javascript mode](https://codemirror.net/mode/javascript/): More TypeScript parsing fixes.
-
-[julia mode](https://codemirror.net/mode/julia/): Fix issue where the mode gets stuck.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Understand cross-line links, parse all bracketed things as links.
-
-[soy mode](https://codemirror.net/mode/soy/): Support single-quoted strings.
-
-[go mode](https://codemirror.net/mode/go/): Don't try to indent inside strings or comments.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Parse line offsets in line or range specs.
-
-## 5.25.2 (2017-04-20)
-
-### Bug fixes
-
-Better handling of selections that cover the whole viewport in contentEditable-mode.
-
-No longer accidentally scroll the editor into view when calling `setValue`.
-
-Work around Chrome Android bug when converting screen coordinates to editor positions.
-
-Make sure long-clicking a selection sets a cursor and doesn't show the editor losing focus.
-
-Fix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Recognize annotations and TypeScript-style type parameters.
-
-[shell mode](https://codemirror.net/mode/shell/): Handle nested braces.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Make parsing of strong/em delimiters CommonMark-compliant.
-
-## 5.25.0 (2017-03-20)
-
-### Bug fixes
-
-In contentEditable-mode, properly locate changes that repeat a character when inserted with IME.
-
-Fix handling of selections bigger than the viewport in contentEditable mode.
-
-Improve handling of changes that insert or delete lines in contentEditable mode.
-
-Count Unicode control characters 0x80 to 0x9F as special (non-printing) chars.
-
-Fix handling of shadow DOM roots when finding the active element.
-
-Add `role=presentation` to more DOM elements to improve screen reader support.
-
-[merge addon](https://codemirror.net/doc/manual.html#addon_merge): Make aligning of unchanged chunks more robust.
-
-[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Fix comment-toggling on a block of text that starts and ends in a (differnet) block comment.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Improve support for TypeScript syntax.
-
-[r mode](https://codemirror.net/mode/r/): Fix indentation after semicolon-less statements.
-
-[shell mode](https://codemirror.net/mode/shell/): Properly handle escaped parentheses in parenthesized expressions.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix a few bugs around leaving fenced code blocks.
-
-[soy mode](https://codemirror.net/mode/soy/): Improve indentation.
-
-### New features
-
-[lint addon](https://codemirror.net/doc/manual.html#addon_lint): Support asynchronous linters that return promises.
-
-[continuelist addon](https://codemirror.net/doc/manual.html#addon_continuelist): Support continuing task lists.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Make Y behave like yy.
-
-[sql mode](https://codemirror.net/mode/sql/): Support sqlite dialect.
-
-## 5.24.2 (2017-02-22)
-
-### Bug fixes
-
-[javascript mode](https://codemirror.net/mode/javascript/): Support computed class method names.
-
-[merge addon](https://codemirror.net/doc/manual.html#addon_merge): Improve aligning of unchanged code in the presence of marks and line widgets.
-
-## 5.24.0 (2017-02-20)
-
-### Bug fixes
-
-A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from.
-
-Visual cursor motion in line-wrapped right-to-left text should be much more correct.
-
-Fix bug in handling of read-only marked text.
-
-[shell mode](https://codemirror.net/mode/shell/): Properly tokenize nested parentheses.
-
-[python mode](https://codemirror.net/mode/python/): Support underscores in number literals.
-
-[sass mode](https://codemirror.net/mode/sass/): Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset.
-
-[css mode](https://codemirror.net/mode/css/): Expose `lineComment` property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.
-
-[julia mode](https://codemirror.net/mode/julia/): Properly indent `elseif` lines.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Properly recognize the end of fenced code blocks when inside other markup.
-
-[scala mode](https://codemirror.net/mode/clike/): Improve handling of operators containing #, @, and : chars.
-
-[xml mode](https://codemirror.net/mode/xml/): Allow dashes in HTML tag names.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Improve parsing of async methods, TypeScript-style comma-separated superclass lists.
-
-[indent-fold addon](https://codemirror.net/demo/folding.html): Ignore comment lines.
-
-### New features
-
-Positions now support a `sticky` property which determines whether they should be associated with the character before (value `"before"`) or after (value `"after"`) them.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Make it possible to remove built-in bindings through the API.
-
-[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings.
-
-### Breaking changes
-
-The [sass mode](https://codemirror.net/mode/sass/) now depends on the [css mode](https://codemirror.net/mode/css/).
-
-## 5.23.0 (2017-01-19)
-
-### Bug fixes
-
-Presentation-related elements DOM elements are now marked as such to help screen readers.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Be more picky about what HTML tags look like to avoid false positives.
-
-### New features
-
-`findModeByMIME` now understands `+json` and `+xml` MIME suffixes.
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Add support for an `override` option to ignore language-specific defaults.
-
-[panel addon](https://codemirror.net/doc/manual.html#addon_panel): Add a `stable` option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.
-
-## 5.22.2 (2017-01-12)
-
-### Bug fixes
-
-Include rollup.config.js in NPM package, so that it can be used to build from source.
-
-## 5.22.0 (2016-12-20)
-
-### Bug fixes
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Make `selectBetweenBrackets` work with multiple cursors.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Fix issues with parsing complex TypeScript types, imports, and exports.
-
-A contentEditable editor instance with autofocus enabled no longer crashes during initializing.
-
-### New features
-
-[emacs bindings](https://codemirror.net/demo/emacs.html): Export `CodeMirror.emacs` to allow other addons to hook into Emacs-style functionality.
-
-[active-line addon](https://codemirror.net/doc/manual.html#addon_active-line): Add `nonEmpty` option.
-
-New event: [`optionChange`](https://codemirror.net/doc/manual.html#event_optionChange).
-
-## 5.21.0 (2016-11-21)
-
-### Bug fixes
-
-Tapping/clicking the editor in [contentEditable mode](https://codemirror.net/doc/manual.html#option_inputStyle) on Chrome now puts the cursor at the tapped position.
-
-Fix various crashes and misbehaviors when reading composition events in [contentEditable mode](https://codemirror.net/doc/manual.html#option_inputStyle).
-
-Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a ``.
-
-[merge addon](https://codemirror.net/doc/manual.html#addon_merge): Fix several issues in the chunk-aligning feature.
-
-[verilog mode](https://codemirror.net/mode/verilog): Rewritten to address various issues.
-
-[julia mode](https://codemirror.net/mode/julia): Recognize Julia 0.5 syntax.
-
-[swift mode](https://codemirror.net/mode/swift): Various fixes and adjustments to current syntax.
-
-[markdown mode](https://codemirror.net/mode/markdown): Allow lists without a blank line above them.
-
-### New features
-
-The [`setGutterMarker`](https://codemirror.net/doc/manual.html#setGutterMarker), [`clearGutter`](https://codemirror.net/doc/manual.html#clearGutter), and [`lineInfo`](https://codemirror.net/doc/manual.html#lineInfo) methods are now available on `Doc` objects.
-
-The [`heightAtLine`](https://codemirror.net/doc/manual.html#heightAtLine) method now takes an extra argument to allow finding the height at the top of the line's line widgets.
-
-[ruby mode](https://codemirror.net/mode/ruby): `else` and `elsif` are now immediately indented.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode.
-
-## 5.20.2 (2016-10-21)
-
-### Bug fixes
-
-Fix `CodeMirror.version` returning the wrong version number.
-
-## 5.20.0 (2016-10-20)
-
-### Bug fixes
-
-Make `newlineAndIndent` command work with multiple cursors on the same line.
-
-Make sure keypress events for backspace are ignored.
-
-Tokens styled with overlays no longer get a nonsense `cm-cm-overlay` class.
-
-Line endings for pasted content are now normalized to the editor's [preferred ending](https://codemirror.net/doc/manual.html#option_lineSeparator).
-
-[javascript mode](https://codemirror.net/mode/javascript): Improve support for class expressions. Support TypeScript optional class properties, the `abstract` keyword, and return type declarations for arrow functions.
-
-[css mode](https://codemirror.net/mode/css): Fix highlighting of mixed-case keywords.
-
-[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Improve behavior when typing a quote before a string.
-
-### New features
-
-The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm run build` (but when installing from NPM, it is included).
-
-The [`refresh`](https://codemirror.net/doc/manual.html#event_refresh) event is now documented and stable.
-
-## 5.19.0 (2016-09-20)
-
-### Bugfixes
-
-[erlang mode](https://codemirror.net/mode/erlang): Fix mode crash when trying to read an empty context.
-
-[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Fix broken behavior when toggling comments inside a comment.
-
-xml-fold addon: Fix a null-dereference bug.
-
-Page up and page down now do something even in single-line documents.
-
-Fix an issue where the cursor position could be off in really long (~8000 character) tokens.
-
-### New features
-
-[javascript mode](https://codemirror.net/mode/javascript): Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the `type` keyword.
-
-The [`blur`](https://codemirror.net/doc/manual.html#event_blur) and [`focus`](https://codemirror.net/doc/manual.html#event_focus) events now pass the DOM event to their handlers.
-
-## 5.18.2 (2016-08-23)
-
-### Bugfixes
-
-[vue mode](https://codemirror.net/mode/vue): Fix outdated references to renamed Pug mode dependency.
-
-## 5.18.0 (2016-08-22)
-
-### Bugfixes
-
-Make sure [gutter backgrounds](https://codemirror.net/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling.
-
-The contenteditable [`inputStyle`](https://codemirror.net/doc/manual.html#option_inputStyle) now properly supports pasting on pre-Edge IE versions.
-
-[javascript mode](https://codemirror.net/mode/javascript): Fix some small parsing bugs and improve TypeScript support.
-
-[matchbrackets addon](https://codemirror.net/doc/manual.html#addon_matchbrackets): Fix bug where active highlighting was left in editor when the addon was disabled.
-
-[match-highlighter addon](https://codemirror.net/doc/manual.html#addon_match-highlighter): Only start highlighting things when the editor gains focus.
-
-[javascript-hint addon](https://codemirror.net/doc/manual.html#addon_javascript-hint): Also complete non-enumerable properties.
-
-### New features
-
-The [`addOverlay`](https://codemirror.net/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied.
-
-MIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined.
-
-### Breaking changes
-
-The mode formerly known as Jade was renamed to [Pug](https://codemirror.net/mode/pug).
-
-The [Python mode](https://codemirror.net/mode/python) now defaults to Python 3 (rather than 2) syntax.
-
-## 5.17.0 (2016-07-19)
-
-### Bugfixes
-
-Fix problem with wrapped trailing whitespace displaying incorrectly.
-
-Prevent IME dialog from overlapping typed content in Chrome.
-
-Improve measuring of characters near a line wrap.
-
-[javascript mode](https://codemirror.net/mode/javascript): Improve support for `async`, allow trailing commas in `import` lists.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Fix backspace in replace mode.
-
-[sublime bindings](https://codemirror.net/demo/sublime.html): Fix some key bindings on OS X to match Sublime Text.
-
-### New features
-
-[markdown mode](https://codemirror.net/mode/markdown): Add more classes to image links in highlight-formatting mode.
-
-## 5.16.0 (2016-06-20)
-
-### Bugfixes
-
-Fix glitches when dragging content caused by the drop indicator receiving mouse events.
-
-Make Control-drag work on Firefox.
-
-Make clicking or selection-dragging at the end of a wrapped line select the right position.
-
-[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Prevent widget scrollbar from hiding part of the hint text.
-
-[rulers addon](https://codemirror.net/doc/manual.html#addon_rulers): Prevent rulers from forcing a horizontal editor scrollbar.
-
-### New features
-
-[search addon](https://codemirror.net/doc/manual.html#addon_search): Automatically bind search-related keys in persistent dialog.
-
-[sublime keymap](https://codemirror.net/demo/sublime.html): Add a multi-cursor aware smart backspace binding.
-
-## 5.15.2 (2016-05-20)
-
-### Bugfixes
-
-Fix a critical document corruption bug that occurs when a document is gradually grown.
-
-## 5.15.0 (2016-05-20)
-
-### Bugfixes
-
-Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode.
-
-Fix issue where not all ASCII control characters were being replaced by placeholders.
-
-Remove the assumption that all modes have a `startState` method from several wrapping modes.
-
-Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any.
-
-Optimize document tree building when loading or pasting huge chunks of content.
-
-[markdown mode](https://codemirror.net/mode/markdown/): Fix several issues in matching link targets.
-
-[clike mode](https://codemirror.net/mode/clike/): Improve indentation of C++ template declarations.
-
-### New features
-
-Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.
-
-Pasting [linewise-copied](https://codemirror.net/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line.
-
-[javascript mode](https://codemirror.net/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax.
-
-## 5.14.2 (2016-04-20)
-
-### Bugfixes
-
-Push a new package to NPM due to an [NPM bug](https://github.com/npm/npm/issues/5082) omitting the LICENSE file in 5.14.0.
-
-Set `dataTransfer.effectAllowed` in `dragstart` handler to help browsers use the right drag icon.
-
-Add the [mbox mode](https://codemirror.net/mode/mbox/index.html) to `mode/meta.js`.
-
-## 5.14.0 (2016-04-20)
-
-### Bugfixes
-
-[`posFromIndex`](https://codemirror.net/doc/manual.html#posFromIndex) and [`indexFromPos`](https://codemirror.net/doc/manual.html#indexFromPos) now take [`lineSeparator`](https://codemirror.net/doc/manual.html#option_lineSeparator) into account.
-
-[vim bindings](https://codemirror.net/demo/vim.html): Only call `.save()` when it is actually available.
-
-[comment addon](https://codemirror.net/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings.
-
-[Python mode](https://codemirror.net/mode/python/index.html): Improve distinguishing of decorators from `@` operators.
-
-[`findMarks`](https://codemirror.net/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range.
-
-### New features
-
-[vim bindings](https://codemirror.net/demo/vim.html): Add yank command.
-
-[match-highlighter addon](https://codemirror.net/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace.
-
-[PowerShell mode](https://codemirror.net/mode/powershell/index.html): Added.
-
-[Yacas mode](https://codemirror.net/mode/yacas/index.html): Added.
-
-[Web IDL mode](https://codemirror.net/mode/webidl/index.html): Added.
-
-[SAS mode](https://codemirror.net/mode/sas/index.html): Added.
-
-[mbox mode](https://codemirror.net/mode/mbox/index.html): Added.
-
-## 5.13.2 (2016-03-23)
-
-### Bugfixes
-
-Solves a problem where the gutter would sometimes not extend all the way to the end of the document.
-
-## 5.13.0 (2016-03-21)
-
-### New features
-
-New DOM event forwarded: [`"dragleave"`](https://codemirror.net/doc/manual.html#event_dom).
-
-[protobuf mode](https://codemirror.net/mode/protobuf/index.html): Newly added.
-
-### Bugfixes
-
-Fix problem where [`findMarks`](https://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks.
-
-Fix crash that showed up when atomic ranges and bidi text were combined.
-
-[show-hint addon](https://codemirror.net/demo/complete.html): Completion widgets no longer close when the line indented or dedented.
-
-[merge addon](https://codemirror.net/demo/merge.html): Fix bug when merging chunks at the end of the file.
-
-[placeholder addon](https://codemirror.net/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](https://codemirror.net/doc/manual.html#swapDoc).
-
-[simplescrollbars addon](https://codemirror.net/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document.
-
-[clike mode](https://codemirror.net/mode/clike/index.html): No longer gets confused when a comment starts after an operator.
-
-[markdown mode](https://codemirror.net/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation.
-
-[dylan mode](https://codemirror.net/mode/dylan/index.html): Several improvements and fixes.
-
-## 5.12.0 (2016-02-19)
-
-### New features
-
-[Vim bindings](https://codemirror.net/demo/vim.html): Ctrl-Q is now an alias for Ctrl-V.
-
-[Vim bindings](https://codemirror.net/demo/vim.html): The Vim API now exposes an `unmap` method to unmap bindings.
-
-[active-line addon](https://codemirror.net/demo/activeline.html): This addon can now style the active line's gutter.
-
-[FCL mode](https://codemirror.net/mode/fcl/): Newly added.
-
-[SQL mode](https://codemirror.net/mode/sql/): Now has a Postgresql dialect.
-
-### Bugfixes
-
-Fix [issue](https://github.com/codemirror/CodeMirror/issues/3781) where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.
-
-Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a [problem](https://github.com/codemirror/CodeMirror/issues/3238) when the editor is inside a transformed parent container.
-
-Solve a [problem](https://github.com/codemirror/CodeMirror/issues/3821) where the horizontal scrollbar could hide text in Firefox.
-
-Fix a [bug](https://github.com/codemirror/CodeMirror/issues/3834) that caused phantom scroll space under the text in some situations.
-
-[Sublime Text bindings](https://codemirror.net/demo/sublime.html): Bind delete-line to Shift-Ctrl-K on OS X.
-
-[Markdown mode](https://codemirror.net/mode/markdown/): Fix [issue](https://github.com/codemirror/CodeMirror/issues/3787) where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.
-
-[Markdown mode](https://codemirror.net/mode/markdown/): Ignore backslashes in code fragments.
-
-[Markdown mode](https://codemirror.net/mode/markdown/): Use whichever mode is registered as `text/html` to parse HTML.
-
-[Clike mode](https://codemirror.net/mode/clike/): Improve indentation of Scala `=>` functions.
-
-[Python mode](https://codemirror.net/mode/python/): Improve indentation of bracketed code.
-
-[HTMLMixed mode](https://codemirror.net/mode/htmlmixed/): Support multi-line opening tags for sub-languages (`
-
-
-
-
-
-
-

Active Line Demo

-
- - - -

Styling the current cursor line.

- - - -
diff --git a/CODE/js/codemirror/demo/anywordhint.html b/CODE/js/codemirror/demo/anywordhint.html deleted file mode 100644 index 471b5bde..00000000 --- a/CODE/js/codemirror/demo/anywordhint.html +++ /dev/null @@ -1,79 +0,0 @@ - - -CodeMirror: Any Word Completion Demo - - - - - - - - - - - -
-

Any Word Completion Demo

-
- -

Press ctrl-space to activate autocompletion. The -completion uses -the anyword-hint.js -module, which simply looks at nearby words in the buffer and completes -to those.

- - -
diff --git a/CODE/js/codemirror/demo/bidi.html b/CODE/js/codemirror/demo/bidi.html deleted file mode 100644 index a7d22d6c..00000000 --- a/CODE/js/codemirror/demo/bidi.html +++ /dev/null @@ -1,106 +0,0 @@ - - -CodeMirror: Bi-directional Text Demo - - - - - - - - - -
-

Bi-directional Text Demo

-
-
- Editor default direction: - - -
-
- HTML document direction: - - -
-
- -
-
- - - -

Demonstration of bi-directional text support. See - the related - blog post for more background.

- -
diff --git a/CODE/js/codemirror/demo/btree.html b/CODE/js/codemirror/demo/btree.html deleted file mode 100644 index 31881014..00000000 --- a/CODE/js/codemirror/demo/btree.html +++ /dev/null @@ -1,83 +0,0 @@ - - -CodeMirror: B-Tree visualization - - - - - - - - -
-

B-Tree visualization

-
-
- - - -

- -
diff --git a/CODE/js/codemirror/demo/buffers.html b/CODE/js/codemirror/demo/buffers.html deleted file mode 100644 index b1f4975c..00000000 --- a/CODE/js/codemirror/demo/buffers.html +++ /dev/null @@ -1,109 +0,0 @@ - - -CodeMirror: Multiple Buffer & Split View Demo - - - - - - - - - - -
-

Multiple Buffer & Split View Demo

- - -
-
- Select buffer: -     -
-
-
- Select buffer: -     -
- - - -

Demonstration of - using linked documents - to provide a split view on a document, and - using swapDoc - to use a single editor to display multiple documents.

- -
diff --git a/CODE/js/codemirror/demo/changemode.html b/CODE/js/codemirror/demo/changemode.html deleted file mode 100644 index da9b0243..00000000 --- a/CODE/js/codemirror/demo/changemode.html +++ /dev/null @@ -1,58 +0,0 @@ - - -CodeMirror: Mode-Changing Demo - - - - - - - - - - -
-

Mode-Changing Demo

-
- -

On changes to the content of the above editor, a (crude) script -tries to auto-detect the language used, and switches the editor to -either JavaScript or Scheme mode based on that.

- - -
diff --git a/CODE/js/codemirror/demo/closebrackets.html b/CODE/js/codemirror/demo/closebrackets.html deleted file mode 100644 index d1415abd..00000000 --- a/CODE/js/codemirror/demo/closebrackets.html +++ /dev/null @@ -1,52 +0,0 @@ - - -CodeMirror: Closebrackets Demo - - - - - - - - - - -
-

Closebrackets Demo

-
- - -
diff --git a/CODE/js/codemirror/demo/closetag.html b/CODE/js/codemirror/demo/closetag.html deleted file mode 100644 index 4f857fa4..00000000 --- a/CODE/js/codemirror/demo/closetag.html +++ /dev/null @@ -1,41 +0,0 @@ - - -CodeMirror: Close-Tag Demo - - - - - - - - - - - - - - -
-

Close-Tag Demo

-
- - -
diff --git a/CODE/js/codemirror/demo/complete.html b/CODE/js/codemirror/demo/complete.html deleted file mode 100644 index 2fef7964..00000000 --- a/CODE/js/codemirror/demo/complete.html +++ /dev/null @@ -1,126 +0,0 @@ - - -CodeMirror: Autocomplete Demo - - - - - - - - - - - - - -
-

Autocomplete Demo

-
- -

Press ctrl-space to activate autocompletion. Built -on top of the show-hint -and javascript-hint -addons.

- -
- - - -
diff --git a/CODE/js/codemirror/demo/emacs.html b/CODE/js/codemirror/demo/emacs.html deleted file mode 100644 index 8f436f8b..00000000 --- a/CODE/js/codemirror/demo/emacs.html +++ /dev/null @@ -1,76 +0,0 @@ - - -CodeMirror: Emacs bindings demo - - - - - - - - - - - - - - - - -
-

Emacs bindings demo

-
- -

The emacs keybindings are enabled by -including keymap/emacs.js and setting -the keyMap option to "emacs". Because -CodeMirror's internal API is quite different from Emacs, they are only -a loose approximation of actual emacs bindings, though.

- -

Also note that a lot of browsers disallow certain keys from being -captured. For example, Chrome blocks both Ctrl-W and Ctrl-N, with the -result that idiomatic use of Emacs keys will constantly close your tab -or open a new window.

- - - -
diff --git a/CODE/js/codemirror/demo/folding.html b/CODE/js/codemirror/demo/folding.html deleted file mode 100644 index 166aa98c..00000000 --- a/CODE/js/codemirror/demo/folding.html +++ /dev/null @@ -1,184 +0,0 @@ - - - - CodeMirror: Code Folding Demo - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Code Folding Demo

-
-
JavaScript:
-
-
HTML:
-
-
JSON with custom widget:
-
-
Python:
-
-
Markdown:
-
-
- -
- diff --git a/CODE/js/codemirror/demo/fullscreen.html b/CODE/js/codemirror/demo/fullscreen.html deleted file mode 100644 index 06bf03e1..00000000 --- a/CODE/js/codemirror/demo/fullscreen.html +++ /dev/null @@ -1,83 +0,0 @@ - - -CodeMirror: Full Screen Editing - - - - - - - - - - - - -
-

Full Screen Editing

-
- - -

Demonstration of - the fullscreen - addon. Press F11 when cursor is in the editor to - toggle full screen editing. Esc can also be used - to exit full screen editing.

-
diff --git a/CODE/js/codemirror/demo/hardwrap.html b/CODE/js/codemirror/demo/hardwrap.html deleted file mode 100644 index 999cf012..00000000 --- a/CODE/js/codemirror/demo/hardwrap.html +++ /dev/null @@ -1,75 +0,0 @@ - - -CodeMirror: Hard-wrapping Demo - - - - - - - - - - -
-

Hard-wrapping Demo

-
- -

Demonstration of -the hardwrap addon. -The above editor has its change event hooked up to -the wrapParagraphsInRange method, so that the paragraphs -are reflown as you are typing.

- - - -
diff --git a/CODE/js/codemirror/demo/html5complete.html b/CODE/js/codemirror/demo/html5complete.html deleted file mode 100644 index 6b908a45..00000000 --- a/CODE/js/codemirror/demo/html5complete.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - CodeMirror: HTML completion demo - - - - - - - - - - - - - - - - - - - -
-

HTML completion demo

- -

Shows the XML completer - parameterized with information about the tags in HTML. - Press ctrl-space to activate completion.

- -
- - -
- diff --git a/CODE/js/codemirror/demo/indentwrap.html b/CODE/js/codemirror/demo/indentwrap.html deleted file mode 100644 index d61f6795..00000000 --- a/CODE/js/codemirror/demo/indentwrap.html +++ /dev/null @@ -1,59 +0,0 @@ - - -CodeMirror: Indented wrapped line demo - - - - - - - - - -
-

Indented wrapped line demo

-
- -

This page uses a hack on top of the "renderLine" - event to make wrapped text line up with the base indentation of - the line.

- - - -
diff --git a/CODE/js/codemirror/demo/lint.html b/CODE/js/codemirror/demo/lint.html deleted file mode 100644 index 1184232d..00000000 --- a/CODE/js/codemirror/demo/lint.html +++ /dev/null @@ -1,171 +0,0 @@ - - -CodeMirror: Linter Demo - - - - - - - - - - - - - - - - - - -
-

Linter Demo

- - -

- -

- -

- - -
diff --git a/CODE/js/codemirror/demo/loadmode.html b/CODE/js/codemirror/demo/loadmode.html deleted file mode 100644 index 263d679c..00000000 --- a/CODE/js/codemirror/demo/loadmode.html +++ /dev/null @@ -1,72 +0,0 @@ - - -CodeMirror: Lazy Mode Loading Demo - - - - - - - - - - -
-

Lazy Mode Loading Demo

-

Current mode: text/plain

-
-

Filename, mime, or mode name:

- - -
diff --git a/CODE/js/codemirror/demo/marker.html b/CODE/js/codemirror/demo/marker.html deleted file mode 100644 index 6744ee3a..00000000 --- a/CODE/js/codemirror/demo/marker.html +++ /dev/null @@ -1,52 +0,0 @@ - - -CodeMirror: Breakpoint Demo - - - - - - - - - -
-

Breakpoint Demo

-
- -

Click the line-number gutter to add or remove 'breakpoints'.

- - - -
diff --git a/CODE/js/codemirror/demo/markselection.html b/CODE/js/codemirror/demo/markselection.html deleted file mode 100644 index 10734b41..00000000 --- a/CODE/js/codemirror/demo/markselection.html +++ /dev/null @@ -1,52 +0,0 @@ - - -CodeMirror: Selection Marking Demo - - - - - - - - - - -
-

Selection Marking Demo

-
- - - -

Simple addon to easily mark (and style) selected text. Docs.

- -
diff --git a/CODE/js/codemirror/demo/matchhighlighter.html b/CODE/js/codemirror/demo/matchhighlighter.html deleted file mode 100644 index 6aa93778..00000000 --- a/CODE/js/codemirror/demo/matchhighlighter.html +++ /dev/null @@ -1,103 +0,0 @@ - - -CodeMirror: Match Highlighter Demo - - - - - - - - - - - - -
-

Match Highlighter Demo

-
- - - -

Search and highlight occurences of the selected text.

- -
diff --git a/CODE/js/codemirror/demo/matchtags.html b/CODE/js/codemirror/demo/matchtags.html deleted file mode 100644 index a5ff6b01..00000000 --- a/CODE/js/codemirror/demo/matchtags.html +++ /dev/null @@ -1,48 +0,0 @@ - - -CodeMirror: Tag Matcher Demo - - - - - - - - - - - -
-

Tag Matcher Demo

- - -
- - - -

Put the cursor on or inside a pair of tags to highlight them. - Press Ctrl-J to jump to the tag that matches the one under the - cursor.

-
diff --git a/CODE/js/codemirror/demo/merge.html b/CODE/js/codemirror/demo/merge.html deleted file mode 100644 index 6eff8134..00000000 --- a/CODE/js/codemirror/demo/merge.html +++ /dev/null @@ -1,123 +0,0 @@ - - -CodeMirror: merge view demo - - - - - - - - - - - - - - - -
-

merge view demo

- - -
- -

The merge -addon provides an interface for displaying and merging diffs, -either two-way -or three-way. -The left (or center) pane is editable, and the differences with the -other pane(s) are optionally shown live as you edit -it. In the two-way configuration, there are also options to pad changed -sections to align them, and to collapse unchanged -stretches of text.

- -

This addon depends on -the google-diff-match-patch -library to compute the diffs.

- - -
diff --git a/CODE/js/codemirror/demo/multiplex.html b/CODE/js/codemirror/demo/multiplex.html deleted file mode 100644 index 13366204..00000000 --- a/CODE/js/codemirror/demo/multiplex.html +++ /dev/null @@ -1,75 +0,0 @@ - - -CodeMirror: Multiplexing Parser Demo - - - - - - - - - - -
-

Multiplexing Parser Demo

-
- - - -

Demonstration of a multiplexing mode, which, at certain - boundary strings, switches to one or more inner modes. The out - (HTML) mode does not get fed the content of the << - >> blocks. See - the manual and - the source for more - information.

- -

- Parsing/Highlighting Tests: - normal, - verbose. -

- -
diff --git a/CODE/js/codemirror/demo/mustache.html b/CODE/js/codemirror/demo/mustache.html deleted file mode 100644 index 2d193f09..00000000 --- a/CODE/js/codemirror/demo/mustache.html +++ /dev/null @@ -1,69 +0,0 @@ - - -CodeMirror: Overlay Parser Demo - - - - - - - - - - -
-

Overlay Parser Demo

-
- - - -

Demonstration of a mode that parses HTML, highlighting - the Mustache templating - directives inside of it by using the code - in overlay.js. View - source to see the 15 lines of code needed to accomplish this.

- -
diff --git a/CODE/js/codemirror/demo/panel.html b/CODE/js/codemirror/demo/panel.html deleted file mode 100644 index d4f813d0..00000000 --- a/CODE/js/codemirror/demo/panel.html +++ /dev/null @@ -1,137 +0,0 @@ - - -CodeMirror: Panel Demo - - - - - - - - - - - - - -
- -

Panel Demo

- -
- -
- -

- The panel - addon allows you to display panels above or below an editor. -
- Click the links below to add panels at the given position: -

- -
-

- top - after-top - before-bottom - bottom -

-

- You can also replace an existing panel: -

-
- - -
- - - -
- -
diff --git a/CODE/js/codemirror/demo/placeholder.html b/CODE/js/codemirror/demo/placeholder.html deleted file mode 100644 index b3bb5fa6..00000000 --- a/CODE/js/codemirror/demo/placeholder.html +++ /dev/null @@ -1,45 +0,0 @@ - - -CodeMirror: Placeholder demo - - - - - - - - - -
-

Placeholder demo

-
- -

The placeholder - plug-in adds an option placeholder that can be set to - make text appear in the editor when it is empty and not focused. - If the source textarea has a placeholder attribute, - it will automatically be inherited.

- - - -
diff --git a/CODE/js/codemirror/demo/preview.html b/CODE/js/codemirror/demo/preview.html deleted file mode 100644 index 660c7921..00000000 --- a/CODE/js/codemirror/demo/preview.html +++ /dev/null @@ -1,87 +0,0 @@ - - -CodeMirror: HTML5 preview - - - - - - - - - - - - -
-

HTML5 preview

- - - - -
diff --git a/CODE/js/codemirror/demo/requirejs.html b/CODE/js/codemirror/demo/requirejs.html deleted file mode 100644 index 0503c921..00000000 --- a/CODE/js/codemirror/demo/requirejs.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - CodeMirror: HTML completion demo - - - - - - - - - - - - -
-

RequireJS module loading demo

- -

This demo does the same thing as - the HTML5 completion demo, but - loads its dependencies - with Require.js, rather than - explicitly. Press ctrl-space to activate - completion.

- -
- - - - -
- diff --git a/CODE/js/codemirror/demo/resize.html b/CODE/js/codemirror/demo/resize.html deleted file mode 100644 index 850f5601..00000000 --- a/CODE/js/codemirror/demo/resize.html +++ /dev/null @@ -1,51 +0,0 @@ - - -CodeMirror: Autoresize Demo - - - - - - - - - -
-

Autoresize Demo

-
- -

By setting an editor's height style -to auto and giving -the viewportMargin -a value of Infinity, CodeMirror can be made to -automatically resize to fit its content.

- - - -
diff --git a/CODE/js/codemirror/demo/rulers.html b/CODE/js/codemirror/demo/rulers.html deleted file mode 100644 index ef12987e..00000000 --- a/CODE/js/codemirror/demo/rulers.html +++ /dev/null @@ -1,49 +0,0 @@ - - -CodeMirror: Ruler Demo - - - - - - - - - -
-

Ruler Demo

- - - -

Demonstration of -the rulers addon, which -displays vertical lines at given column offsets.

- -
diff --git a/CODE/js/codemirror/demo/runmode-standalone.html b/CODE/js/codemirror/demo/runmode-standalone.html deleted file mode 100644 index f3503b98..00000000 --- a/CODE/js/codemirror/demo/runmode-standalone.html +++ /dev/null @@ -1,61 +0,0 @@ - - -CodeMirror: Mode Runner Demo - - - - - - - - -
-

Mode Runner Demo

- - -
- -

-
-    
-
-    

Running a CodeMirror mode outside of the editor. - The CodeMirror.runMode function, defined - in addon/runmode/runmode.js takes the following arguments:

- -
-
text (string)
-
The document to run through the highlighter.
-
mode (mode spec)
-
The mode to use (must be loaded as normal).
-
output (function or DOM node)
-
If this is a function, it will be called for each token with - two arguments, the token's text and the token's style class (may - be null for unstyled tokens). If it is a DOM node, - the tokens will be converted to span elements as in - an editor, and inserted into the node - (through innerHTML).
-
- -
diff --git a/CODE/js/codemirror/demo/runmode.html b/CODE/js/codemirror/demo/runmode.html deleted file mode 100644 index 0284c405..00000000 --- a/CODE/js/codemirror/demo/runmode.html +++ /dev/null @@ -1,62 +0,0 @@ - - -CodeMirror: Mode Runner Demo - - - - - - - - - -
-

Mode Runner Demo

- - -
- -

-
-    
-
-    

Running a CodeMirror mode outside of the editor. - The CodeMirror.runMode function, defined - in addon/runmode/runmode.js takes the following arguments:

- -
-
text (string)
-
The document to run through the highlighter.
-
mode (mode spec)
-
The mode to use (must be loaded as normal).
-
output (function or DOM node)
-
If this is a function, it will be called for each token with - two arguments, the token's text and the token's style class (may - be null for unstyled tokens). If it is a DOM node, - the tokens will be converted to span elements as in - an editor, and inserted into the node - (through innerHTML).
-
- -
diff --git a/CODE/js/codemirror/demo/search.html b/CODE/js/codemirror/demo/search.html deleted file mode 100644 index 3abb0c78..00000000 --- a/CODE/js/codemirror/demo/search.html +++ /dev/null @@ -1,99 +0,0 @@ - - -CodeMirror: Search/Replace Demo - - - - - - - - - - - - - - - - - -
-

Search/Replace Demo

-
- - - -

Demonstration of primitive search/replace functionality. The - keybindings (which can be configured with custom keymaps) are:

-
-
Ctrl-F / Cmd-F
Start searching
-
Ctrl-G / Cmd-G
Find next
-
Shift-Ctrl-G / Shift-Cmd-G
Find previous
-
Shift-Ctrl-F / Cmd-Option-F
Replace
-
Shift-Ctrl-R / Shift-Cmd-Option-F
Replace all
-
Alt-F
Persistent search (dialog doesn't autoclose, - enter to find next, Shift-Enter to find previous)
-
Alt-G
Jump to line
-
-

Searching is enabled by - including addon/search/search.js - and addon/search/searchcursor.js. - Jump to line - including addon/search/jump-to-line.js.

-

For good-looking input dialogs, you also want to include - addon/dialog/dialog.js - and addon/dialog/dialog.css.

-
diff --git a/CODE/js/codemirror/demo/simplemode.html b/CODE/js/codemirror/demo/simplemode.html deleted file mode 100644 index d7b0cfac..00000000 --- a/CODE/js/codemirror/demo/simplemode.html +++ /dev/null @@ -1,186 +0,0 @@ - - -CodeMirror: Simple Mode Demo - - - - - - - - - - - -
-

Simple Mode Demo

- -

The mode/simple -addon allows CodeMirror modes to be specified using a relatively simple -declarative format. This format is not as powerful as writing code -directly against the mode -interface, but is a lot easier to get started with, and -sufficiently expressive for many simple language modes.

- -

This interface is still in flux. It is unlikely to be scrapped or -overhauled completely, so do start writing code against it, but -details might change as it stabilizes, and you might have to tweak -your code when upgrading.

- -

Simple modes (loosely based on -the Common -JavaScript Syntax Highlighting Specification, which never took -off), are state machines, where each state has a number of rules that -match tokens. A rule describes a type of token that may occur in the -current state, and possibly a transition to another state caused by -that token.

- -

The CodeMirror.defineSimpleMode(name, states) method -takes a mode name and an object that describes the mode's states. The -editor below shows an example of such a mode (and is itself -highlighted by the mode shown in it).

- -
- -

Each state is an array of rules. A rule may have the following properties:

- -
-
regex: string | RegExp
-
The regular expression that matches the token. May be a string - or a regex object. When a regex, the ignoreCase flag - will be taken into account when matching the token. This regex - has to capture groups when the token property is - an array. If it captures groups, it must capture all of the string - (since JS provides no way to find out where a group matched). - Currently negative lookbehind assertion for regex is not supported, regardless of browser support.
-
token: string | array<string> | null
-
An optional token style. Multiple styles can be specified by - separating them with dots or spaces. When this property holds an array of token styles, - the regex for this rule must capture a group for each array item. -
-
sol: boolean
-
When true, this token will only match at the start of the line. - (The ^ regexp marker doesn't work as you'd expect in - this context because of limitations in JavaScript's RegExp - API.)
-
next: string
-
When a next property is present, the mode will - transfer to the state named by the property when the token is - encountered.
-
push: string
-
Like next, but instead replacing the current state - by the new state, the current state is kept on a stack, and can be - returned to with the pop directive.
-
pop: bool
-
When true, and there is another state on the state stack, will - cause the mode to pop that state off the stack and transition to - it.
-
mode: {spec, end, persistent}
-
Can be used to embed another mode inside a mode. When present, - must hold an object with a spec property that describes - the embedded mode, and an optional end end property - that specifies the regexp that will end the extent of the mode. When - a persistent property is set (and true), the nested - mode's state will be preserved between occurrences of the mode.
-
indent: bool
-
When true, this token changes the indentation to be one unit - more than the current line's indentation.
-
dedent: bool
-
When true, this token will pop one scope off the indentation - stack.
-
dedentIfLineStart: bool
-
If a token has its dedent property set, it will, by - default, cause lines where it appears at the start to be dedented. - Set this property to false to prevent that behavior.
-
- -

The meta property of the states object is special, and -will not be interpreted as a state. Instead, properties set on it will -be set on the mode, which is useful for properties -like lineComment, -which sets the comment style for a mode. The simple mode addon also -recognizes a few such properties:

- -
-
dontIndentStates: array<string>
-
An array of states in which the mode's auto-indentation should - not take effect. Usually used for multi-line comment and string - states.
-
- - - - - -
diff --git a/CODE/js/codemirror/demo/simplescrollbars.html b/CODE/js/codemirror/demo/simplescrollbars.html deleted file mode 100644 index 16042978..00000000 --- a/CODE/js/codemirror/demo/simplescrollbars.html +++ /dev/null @@ -1,82 +0,0 @@ - - -CodeMirror: Simple Scrollbar Demo - - - - - - - - - - - - -
-

Simple Scrollbar Demo

-
- -

The simplescrollbars addon defines two -styles of non-native scrollbars: "simple" and "overlay" (click to try), which can be passed to -the scrollbarStyle option. These implement -the scrollbar using DOM elements, allowing more control over -its appearance.

- - -
diff --git a/CODE/js/codemirror/demo/spanaffectswrapping_shim.html b/CODE/js/codemirror/demo/spanaffectswrapping_shim.html deleted file mode 100644 index 46667ddc..00000000 --- a/CODE/js/codemirror/demo/spanaffectswrapping_shim.html +++ /dev/null @@ -1,85 +0,0 @@ - - -CodeMirror: Automatically derive odd wrapping behavior for your browser - - - - - -
-

Automatically derive odd wrapping behavior for your browser

- - -

This is a hack to automatically derive - a spanAffectsWrapping regexp for a browser. See the - comments above that variable - in lib/codemirror.js - for some more details.

- -
-

-
-    
-  
diff --git a/CODE/js/codemirror/demo/sublime.html b/CODE/js/codemirror/demo/sublime.html deleted file mode 100644 index bfe17c56..00000000 --- a/CODE/js/codemirror/demo/sublime.html +++ /dev/null @@ -1,77 +0,0 @@ - - -CodeMirror: Sublime Text bindings demo - - - - - - - - - - - - - - - - - - - - - - -
-

Sublime Text bindings demo

- -

The sublime keymap defines many Sublime Text-specific -bindings for CodeMirror. See the code below for an overview.

- -

Enable the keymap by -loading keymap/sublime.js -and setting -the keyMap -option to "sublime".

- -

(A lot of the search functionality is still missing.) - - - -

diff --git a/CODE/js/codemirror/demo/tern.html b/CODE/js/codemirror/demo/tern.html deleted file mode 100644 index e331fd5c..00000000 --- a/CODE/js/codemirror/demo/tern.html +++ /dev/null @@ -1,133 +0,0 @@ - - -CodeMirror: Tern Demo - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Tern Demo

-
- -

Demonstrates integration of Tern -and CodeMirror. The following keys are bound:

- -
-
Ctrl-Space
Autocomplete
-
Ctrl-O
Find docs for the expression at the cursor
-
Ctrl-I
Find type at cursor
-
Alt-.
Jump to definition (Alt-, to jump back)
-
Ctrl-Q
Rename variable
-
Ctrl-.
Select all occurrences of a variable
-
- -

Documentation is sparse for now. See the top of -the script for a rough API -overview.

- - - -
diff --git a/CODE/js/codemirror/demo/theme.html b/CODE/js/codemirror/demo/theme.html deleted file mode 100644 index 95d0f391..00000000 --- a/CODE/js/codemirror/demo/theme.html +++ /dev/null @@ -1,196 +0,0 @@ - - -CodeMirror: Theme Demo - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Theme Demo

-
- -

Select a theme: -

- - -
diff --git a/CODE/js/codemirror/demo/trailingspace.html b/CODE/js/codemirror/demo/trailingspace.html deleted file mode 100644 index fb553994..00000000 --- a/CODE/js/codemirror/demo/trailingspace.html +++ /dev/null @@ -1,48 +0,0 @@ - - -CodeMirror: Trailing Whitespace Demo - - - - - - - - - -
-

Trailing Whitespace Demo

-
- - - -

Uses -the trailingspace -addon to highlight trailing whitespace.

- -
diff --git a/CODE/js/codemirror/demo/variableheight.html b/CODE/js/codemirror/demo/variableheight.html deleted file mode 100644 index a3af5cf4..00000000 --- a/CODE/js/codemirror/demo/variableheight.html +++ /dev/null @@ -1,67 +0,0 @@ - - -CodeMirror: Variable Height Demo - - - - - - - - - - -
-

Variable Height Demo

-
- -
diff --git a/CODE/js/codemirror/demo/vim.html b/CODE/js/codemirror/demo/vim.html deleted file mode 100644 index 172ab6f0..00000000 --- a/CODE/js/codemirror/demo/vim.html +++ /dev/null @@ -1,117 +0,0 @@ - - -CodeMirror: Vim bindings demo - - - - - - - - - - - - - - - - -
-

Vim bindings demo

- -

Note: The CodeMirror vim bindings do not have an -active maintainer. That means that if you report bugs in it, they are -likely to go unanswered. It also means that if you want to help, you -are very welcome to look -at the -open issues and see which ones you can solve.

- -
-
Key buffer:
-
Vim mode:
- -

The vim keybindings are enabled by including keymap/vim.js and setting the -keyMap option to vim.

- -

Features

- -
    -
  • All common motions and operators, including text objects
  • -
  • Operator motion orthogonality
  • -
  • Visual mode - characterwise, linewise, blockwise
  • -
  • Full macro support (q, @)
  • -
  • Incremental highlighted search (/, ?, #, *, g#, g*)
  • -
  • Search/replace with confirm (:substitute, :%s)
  • -
  • Search history
  • -
  • Jump lists (Ctrl-o, Ctrl-i)
  • -
  • Key/command mapping with API (:map, :nmap, :vmap)
  • -
  • Sort (:sort)
  • -
  • Marks (`, ')
  • -
  • :global
  • -
  • Insert mode behaves identical to base CodeMirror
  • -
  • Cross-buffer yank/paste
  • -
- -

For the full list of key mappings and Ex commands, refer to the -defaultKeymap and defaultExCommandMap at the -top of keymap/vim.js. - -

Note that while the vim mode tries to emulate the most useful -features of vim as faithfully as possible, it does not strive to -become a complete vim implementation

- - - -
diff --git a/CODE/js/codemirror/demo/visibletabs.html b/CODE/js/codemirror/demo/visibletabs.html deleted file mode 100644 index f4f5a783..00000000 --- a/CODE/js/codemirror/demo/visibletabs.html +++ /dev/null @@ -1,62 +0,0 @@ - - -CodeMirror: Visible tabs demo - - - - - - - - - -
-

Visible tabs demo

-
- -

Tabs inside the editor are spans with the -class cm-tab, and can be styled.

- - - -
diff --git a/CODE/js/codemirror/demo/widget.html b/CODE/js/codemirror/demo/widget.html deleted file mode 100644 index 6fae2d2f..00000000 --- a/CODE/js/codemirror/demo/widget.html +++ /dev/null @@ -1,85 +0,0 @@ - - -CodeMirror: Inline Widget Demo - - - - - - - - - - -
-

Inline Widget Demo

- - -
- -

This demo runs JSHint over the code -in the editor (which is the script used on this page), and -inserts line widgets to -display the warnings that JSHint comes up with.

-
diff --git a/CODE/js/codemirror/demo/xmlcomplete.html b/CODE/js/codemirror/demo/xmlcomplete.html deleted file mode 100644 index dbbc9bac..00000000 --- a/CODE/js/codemirror/demo/xmlcomplete.html +++ /dev/null @@ -1,119 +0,0 @@ - - -CodeMirror: XML Autocomplete Demo - - - - - - - - - - - - -
-

XML Autocomplete Demo

-
- -

Press ctrl-space, or type a '<' character to - activate autocompletion. This demo defines a simple schema that - guides completion. The schema can be customized—see - the manual.

- -

Development of the xml-hint addon was kindly - sponsored - by www.xperiment.mobi.

- - -
diff --git a/CODE/js/codemirror/doc/activebookmark.js b/CODE/js/codemirror/doc/activebookmark.js deleted file mode 100644 index 407282d0..00000000 --- a/CODE/js/codemirror/doc/activebookmark.js +++ /dev/null @@ -1,57 +0,0 @@ -// Kludge in HTML5 tag recognition in IE8 -document.createElement("section"); -document.createElement("article"); - -(function() { - if (!window.addEventListener) return; - var pending = false, prevVal = null; - - function updateSoon() { - if (!pending) { - pending = true; - setTimeout(update, 250); - } - } - - function update() { - pending = false; - var marks = document.getElementById("nav").getElementsByTagName("a"), found; - for (var i = 0; i < marks.length; ++i) { - var mark = marks[i], m; - if (mark.getAttribute("data-default")) { - if (found == null) found = i; - } else if (m = mark.href.match(/#(.*)/)) { - var ref = document.getElementById(m[1]); - if (ref && ref.getBoundingClientRect().top < 50) - found = i; - } - } - if (found != null && found != prevVal) { - prevVal = found; - var lis = document.getElementById("nav").getElementsByTagName("li"); - for (var i = 0; i < lis.length; ++i) lis[i].className = ""; - for (var i = 0; i < marks.length; ++i) { - if (found == i) { - marks[i].className = "active"; - for (var n = marks[i]; n; n = n.parentNode) - if (n.nodeName == "LI") n.className = "active"; - } else { - marks[i].className = ""; - } - } - } - } - - window.addEventListener("scroll", updateSoon); - window.addEventListener("load", updateSoon); - window.addEventListener("hashchange", function() { - setTimeout(function() { - var hash = document.location.hash, found = null, m; - var marks = document.getElementById("nav").getElementsByTagName("a"); - for (var i = 0; i < marks.length; i++) - if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) { found = i; break; } - if (found != null) for (var i = 0; i < marks.length; i++) - marks[i].className = i == found ? "active" : ""; - }, 300); - }); -})(); diff --git a/CODE/js/codemirror/doc/docs.css b/CODE/js/codemirror/doc/docs.css deleted file mode 100644 index 17b92124..00000000 --- a/CODE/js/codemirror/doc/docs.css +++ /dev/null @@ -1,274 +0,0 @@ -@font-face { - font-family: 'Source Sans Pro'; - font-style: normal; - font-weight: 400; - src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(//themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff) format('woff'); -} - -body, html { margin: 0; padding: 0; height: 100%; } -section, article { display: block; padding: 0; } - -body { - background: #f8f8f8; - font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; - line-height: 1.5; -} - -p { margin-top: 0; } - -h2, h3, h1 { - font-weight: normal; - margin-bottom: .7em; -} -h1 { font-size: 140%; } -h2 { font-size: 120%; } -h3 { font-size: 110%; } -article > h2:first-child, section:first-child > h2 { margin-top: 0; } - -#nav h1 { - margin-right: 12px; - margin-top: 0; - margin-bottom: 2px; - color: #d30707; - letter-spacing: .5px; -} - -a, a:visited, a:link, .quasilink { - color: #A21313; -} - -em { - padding-right: 2px; -} - -.quasilink { - cursor: pointer; -} - -article { - max-width: 700px; - margin: 0 0 0 160px; - border-left: 2px solid #E30808; - border-right: 1px solid #ddd; - padding: 30px 50px 100px 50px; - background: white; - z-index: 2; - position: relative; - min-height: 100%; - box-sizing: border-box; - -moz-box-sizing: border-box; -} - -#nav { - position: fixed; - padding-top: 30px; - max-height: 100%; - box-sizing: -moz-border-box; - box-sizing: border-box; - overflow-y: auto; - left: 0; right: none; - width: 160px; - text-align: right; - z-index: 1; -} - -@media screen and (min-width: 1000px) { - article { - margin: 0 auto; - } - #nav { - right: 50%; - width: auto; - border-right: 349px solid transparent; - } -} - -#nav ul { - display: block; - margin: 0; padding: 0; - margin-bottom: 32px; -} - -#nav a { - text-decoration: none; -} - -#nav li { - display: block; - margin-bottom: 4px; -} - -#nav li ul { - font-size: 80%; - margin-bottom: 0; - display: none; -} - -#nav li.active ul { - display: block; -} - -#nav li li a { - padding-right: 20px; - display: inline-block; -} - -#nav ul a { - color: black; - padding: 0 7px 1px 11px; -} - -#nav ul a.active, #nav ul a:hover { - border-bottom: 1px solid #E30808; - margin-bottom: -1px; - color: #E30808; -} - -#logo { - border: 0; - margin-right: 12px; - margin-bottom: 25px; -} - -section { - border-top: 1px solid #E30808; - margin: 1.5em 0; -} - -section.first { - border: none; - margin-top: 0; -} - -#demo { - position: relative; -} - -#demolist { - position: absolute; - right: 5px; - top: 5px; - z-index: 25; -} - -.yinyang { - position: absolute; - top: -10px; - left: 0; right: 0; - margin: auto; - display: block; - height: 120px; -} - -.actions { - margin: 1em 0 0; - min-height: 100px; - position: relative; -} - -.actionspicture { - pointer-events: none; - position: absolute; - height: 100px; - top: 0; left: 0; right: 0; -} - -.actionlink { - pointer-events: auto; - font-family: arial; - font-size: 80%; - font-weight: bold; - position: absolute; - top: 0; bottom: 0; - line-height: 1; - height: 1em; - margin: auto; -} - -.actionlink.download { - color: white; - right: 50%; - margin-right: 13px; - text-shadow: -1px 1px 3px #b00, -1px -1px 3px #b00, 1px 0px 3px #b00; -} - -.actionlink.fund { - color: #b00; - left: 50%; - margin-left: 15px; -} - -.actionlink:hover { - text-decoration: underline; -} - -.actionlink a { - color: inherit; -} - -.actionsleft { - float: left; -} - -.actionsright { - float: right; - text-align: right; -} - -@media screen and (max-width: 800px) { - .actions { - padding-top: 120px; - } - .actionsleft, .actionsright { - float: none; - text-align: left; - margin-bottom: 1em; - } -} - -th { - text-decoration: underline; - font-weight: normal; - text-align: left; -} - -#features ul { - list-style: none; - margin: 0 0 1em; - padding: 0 0 0 1.2em; -} - -#features li:before { - content: "-"; - width: 1em; - display: inline-block; - padding: 0; - margin: 0; - margin-left: -1em; -} - -.rel { - margin-bottom: 0; -} -.rel-note { - margin-top: 0; - color: #555; -} - -pre { - padding-left: 15px; - border-left: 2px solid #ddd; -} - -code { - padding: 0 2px; -} - -strong { - text-decoration: underline; - font-weight: normal; -} - -.field { - border: 1px solid #A21313; -} diff --git a/CODE/js/codemirror/doc/internals.html b/CODE/js/codemirror/doc/internals.html deleted file mode 100644 index 2137c937..00000000 --- a/CODE/js/codemirror/doc/internals.html +++ /dev/null @@ -1,504 +0,0 @@ - - -CodeMirror: Internals - - - - - - - -
- -

(Re-) Implementing A Syntax-Highlighting Editor in JavaScript

- -

- Topic: JavaScript, code editor implementation
- Author: Marijn Haverbeke
- Date: March 2nd 2011 (updated November 13th 2011) -

- -

Caution: this text was written briefly after -version 2 was initially written. It no longer (even including the -update at the bottom) fully represents the current implementation. I'm -leaving it here as a historic document. For more up-to-date -information, look at the entries -tagged cm-internals -on my blog.

- -

This is a followup to -my Brutal Odyssey to the -Dark Side of the DOM Tree story. That one describes the -mind-bending process of implementing (what would become) CodeMirror 1. -This one describes the internals of CodeMirror 2, a complete rewrite -and rethink of the old code base. I wanted to give this piece another -Hunter Thompson copycat subtitle, but somehow that would be out of -place—the process this time around was one of straightforward -engineering, requiring no serious mind-bending whatsoever.

- -

So, what is wrong with CodeMirror 1? I'd estimate, by mailing list -activity and general search-engine presence, that it has been -integrated into about a thousand systems by now. The most prominent -one, since a few weeks, -being Google -code's project hosting. It works, and it's being used widely.

- -

Still, I did not start replacing it because I was bored. CodeMirror -1 was heavily reliant on designMode -or contentEditable (depending on the browser). Neither of -these are well specified (HTML5 tries -to specify -their basics), and, more importantly, they tend to be one of the more -obscure and buggy areas of browser functionality—CodeMirror, by using -this functionality in a non-typical way, was constantly running up -against browser bugs. WebKit wouldn't show an empty line at the end of -the document, and in some releases would suddenly get unbearably slow. -Firefox would show the cursor in the wrong place. Internet Explorer -would insist on linkifying everything that looked like a URL or email -address, a behaviour that can't be turned off. Some bugs I managed to -work around (which was often a frustrating, painful process), others, -such as the Firefox cursor placement, I gave up on, and had to tell -user after user that they were known problems, but not something I -could help.

- -

Also, there is the fact that designMode (which seemed -to be less buggy than contentEditable in Webkit and -Firefox, and was thus used by CodeMirror 1 in those browsers) requires -a frame. Frames are another tricky area. It takes some effort to -prevent getting tripped up by domain restrictions, they don't -initialize synchronously, behave strangely in response to the back -button, and, on several browsers, can't be moved around the DOM -without having them re-initialize. They did provide a very nice way to -namespace the library, though—CodeMirror 1 could freely pollute the -namespace inside the frame.

- -

Finally, working with an editable document means working with -selection in arbitrary DOM structures. Internet Explorer (8 and -before) has an utterly different (and awkward) selection API than all -of the other browsers, and even among the different implementations of -document.selection, details about how exactly a selection -is represented vary quite a bit. Add to that the fact that Opera's -selection support tended to be very buggy until recently, and you can -imagine why CodeMirror 1 contains 700 lines of selection-handling -code.

- -

And that brings us to the main issue with the CodeMirror 1 -code base: The proportion of browser-bug-workarounds to real -application code was getting dangerously high. By building on top of a -few dodgy features, I put the system in a vulnerable position—any -incompatibility and bugginess in these features, I had to paper over -with my own code. Not only did I have to do some serious stunt-work to -get it to work on older browsers (as detailed in the -previous story), things -also kept breaking in newly released versions, requiring me to come up -with new scary hacks in order to keep up. This was starting -to lose its appeal.

- -
-

General Approach

- -

What CodeMirror 2 does is try to sidestep most of the hairy hacks -that came up in version 1. I owe a lot to the -ACE editor for inspiration on how to -approach this.

- -

I absolutely did not want to be completely reliant on key events to -generate my input. Every JavaScript programmer knows that key event -information is horrible and incomplete. Some people (most awesomely -Mihai Bazon with Ymacs) have been able -to build more or less functioning editors by directly reading key -events, but it takes a lot of work (the kind of never-ending, fragile -work I described earlier), and will never be able to properly support -things like multi-keystoke international character -input. [see below for caveat]

- -

So what I do is focus a hidden textarea, and let the browser -believe that the user is typing into that. What we show to the user is -a DOM structure we built to represent his document. If this is updated -quickly enough, and shows some kind of believable cursor, it feels -like a real text-input control.

- -

Another big win is that this DOM representation does not have to -span the whole document. Some CodeMirror 1 users insisted that they -needed to put a 30 thousand line XML document into CodeMirror. Putting -all that into the DOM takes a while, especially since, for some -reason, an editable DOM tree is slower than a normal one on most -browsers. If we have full control over what we show, we must only -ensure that the visible part of the document has been added, and can -do the rest only when needed. (Fortunately, the onscroll -event works almost the same on all browsers, and lends itself well to -displaying things only as they are scrolled into view.)

-
-
-

Input

- -

ACE uses its hidden textarea only as a text input shim, and does -all cursor movement and things like text deletion itself by directly -handling key events. CodeMirror's way is to let the browser do its -thing as much as possible, and not, for example, define its own set of -key bindings. One way to do this would have been to have the whole -document inside the hidden textarea, and after each key event update -the display DOM to reflect what's in that textarea.

- -

That'd be simple, but it is not realistic. For even medium-sized -document the editor would be constantly munging huge strings, and get -terribly slow. What CodeMirror 2 does is put the current selection, -along with an extra line on the top and on the bottom, into the -textarea.

- -

This means that the arrow keys (and their ctrl-variations), home, -end, etcetera, do not have to be handled specially. We just read the -cursor position in the textarea, and update our cursor to match it. -Also, copy and paste work pretty much for free, and people get their -native key bindings, without any special work on my part. For example, -I have emacs key bindings configured for Chrome and Firefox. There is -no way for a script to detect this. [no longer the case]

- -

Of course, since only a small part of the document sits in the -textarea, keys like page up and ctrl-end won't do the right thing. -CodeMirror is catching those events and handling them itself.

-
-
-

Selection

- -

Getting and setting the selection range of a textarea in modern -browsers is trivial—you just use the selectionStart -and selectionEnd properties. On IE you have to do some -insane stuff with temporary ranges and compensating for the fact that -moving the selection by a 'character' will treat \r\n as a single -character, but even there it is possible to build functions that -reliably set and get the selection range.

- -

But consider this typical case: When I'm somewhere in my document, -press shift, and press the up arrow, something gets selected. Then, if -I, still holding shift, press the up arrow again, the top of my -selection is adjusted. The selection remembers where its head -and its anchor are, and moves the head when we shift-move. -This is a generally accepted property of selections, and done right by -every editing component built in the past twenty years.

- -

But not something that the browser selection APIs expose.

- -

Great. So when someone creates an 'upside-down' selection, the next -time CodeMirror has to update the textarea, it'll re-create the -selection as an 'upside-up' selection, with the anchor at the top, and -the next cursor motion will behave in an unexpected way—our second -up-arrow press in the example above will not do anything, since it is -interpreted in exactly the same way as the first.

- -

No problem. We'll just, ehm, detect that the selection is -upside-down (you can tell by the way it was created), and then, when -an upside-down selection is present, and a cursor-moving key is -pressed in combination with shift, we quickly collapse the selection -in the textarea to its start, allow the key to take effect, and then -combine its new head with its old anchor to get the real -selection.

- -

In short, scary hacks could not be avoided entirely in CodeMirror -2.

- -

And, the observant reader might ask, how do you even know that a -key combo is a cursor-moving combo, if you claim you support any -native key bindings? Well, we don't, but we can learn. The editor -keeps a set known cursor-movement combos (initialized to the -predictable defaults), and updates this set when it observes that -pressing a certain key had (only) the effect of moving the cursor. -This, of course, doesn't work if the first time the key is used was -for extending an inverted selection, but it works most of the -time.

-
-
-

Intelligent Updating

- -

One thing that always comes up when you have a complicated internal -state that's reflected in some user-visible external representation -(in this case, the displayed code and the textarea's content) is -keeping the two in sync. The naive way is to just update the display -every time you change your state, but this is not only error prone -(you'll forget), it also easily leads to duplicate work on big, -composite operations. Then you start passing around flags indicating -whether the display should be updated in an attempt to be efficient -again and, well, at that point you might as well give up completely.

- -

I did go down that road, but then switched to a much simpler model: -simply keep track of all the things that have been changed during an -action, and then, only at the end, use this information to update the -user-visible display.

- -

CodeMirror uses a concept of operations, which start by -calling a specific set-up function that clears the state and end by -calling another function that reads this state and does the required -updating. Most event handlers, and all the user-visible methods that -change state are wrapped like this. There's a method -called operation that accepts a function, and returns -another function that wraps the given function as an operation.

- -

It's trivial to extend this (as CodeMirror does) to detect nesting, -and, when an operation is started inside an operation, simply -increment the nesting count, and only do the updating when this count -reaches zero again.

- -

If we have a set of changed ranges and know the currently shown -range, we can (with some awkward code to deal with the fact that -changes can add and remove lines, so we're dealing with a changing -coordinate system) construct a map of the ranges that were left -intact. We can then compare this map with the part of the document -that's currently visible (based on scroll offset and editor height) to -determine whether something needs to be updated.

- -

CodeMirror uses two update algorithms—a full refresh, where it just -discards the whole part of the DOM that contains the edited text and -rebuilds it, and a patch algorithm, where it uses the information -about changed and intact ranges to update only the out-of-date parts -of the DOM. When more than 30 percent (which is the current heuristic, -might change) of the lines need to be updated, the full refresh is -chosen (since it's faster to do than painstakingly finding and -updating all the changed lines), in the other case it does the -patching (so that, if you scroll a line or select another character, -the whole screen doesn't have to be -re-rendered). [the full-refresh -algorithm was dropped, it wasn't really faster than the patching -one]

- -

All updating uses innerHTML rather than direct DOM -manipulation, since that still seems to be by far the fastest way to -build documents. There's a per-line function that combines the -highlighting, marking, and -selection info for that line into a snippet of HTML. The patch updater -uses this to reset individual lines, the refresh updater builds an -HTML chunk for the whole visible document at once, and then uses a -single innerHTML update to do the refresh.

-
-
-

Parsers can be Simple

- -

When I wrote CodeMirror 1, I -thought interruptable -parsers were a hugely scary and complicated thing, and I used a -bunch of heavyweight abstractions to keep this supposed complexity -under control: parsers -were iterators -that consumed input from another iterator, and used funny -closure-resetting tricks to copy and resume themselves.

- -

This made for a rather nice system, in that parsers formed strictly -separate modules, and could be composed in predictable ways. -Unfortunately, it was quite slow (stacking three or four iterators on -top of each other), and extremely intimidating to people not used to a -functional programming style.

- -

With a few small changes, however, we can keep all those -advantages, but simplify the API and make the whole thing less -indirect and inefficient. CodeMirror -2's mode API uses explicit state -objects, and makes the parser/tokenizer a function that simply takes a -state and a character stream abstraction, advances the stream one -token, and returns the way the token should be styled. This state may -be copied, optionally in a mode-defined way, in order to be able to -continue a parse at a given point. Even someone who's never touched a -lambda in his life can understand this approach. Additionally, far -fewer objects are allocated in the course of parsing now.

- -

The biggest speedup comes from the fact that the parsing no longer -has to touch the DOM though. In CodeMirror 1, on an older browser, you -could see the parser work its way through the document, -managing some twenty lines in each 50-millisecond time slice it got. It -was reading its input from the DOM, and updating the DOM as it went -along, which any experienced JavaScript programmer will immediately -spot as a recipe for slowness. In CodeMirror 2, the parser usually -finishes the whole document in a single 100-millisecond time slice—it -manages some 1500 lines during that time on Chrome. All it has to do -is munge strings, so there is no real reason for it to be slow -anymore.

-
-
-

What Gives?

- -

Given all this, what can you expect from CodeMirror 2?

- -
    - -
  • Small. the base library is -some 45k when minified -now, 17k when gzipped. It's smaller than -its own logo.
  • - -
  • Lightweight. CodeMirror 2 initializes very -quickly, and does almost no work when it is not focused. This means -you can treat it almost like a textarea, have multiple instances on a -page without trouble.
  • - -
  • Huge document support. Since highlighting is -really fast, and no DOM structure is being built for non-visible -content, you don't have to worry about locking up your browser when a -user enters a megabyte-sized document.
  • - -
  • Extended API. Some things kept coming up in the -mailing list, such as marking pieces of text or lines, which were -extremely hard to do with CodeMirror 1. The new version has proper -support for these built in.
  • - -
  • Tab support. Tabs inside editable documents were, -for some reason, a no-go. At least six different people announced they -were going to add tab support to CodeMirror 1, none survived (I mean, -none delivered a working version). CodeMirror 2 no longer removes tabs -from your document.
  • - -
  • Sane styling. iframe nodes aren't -really known for respecting document flow. Now that an editor instance -is a plain div element, it is much easier to size it to -fit the surrounding elements. You don't even have to make it scroll if -you do not want to.
  • - -
- -

On the downside, a CodeMirror 2 instance is not a native -editable component. Though it does its best to emulate such a -component as much as possible, there is functionality that browsers -just do not allow us to hook into. Doing select-all from the context -menu, for example, is not currently detected by CodeMirror.

- -

[Updates from November 13th 2011] Recently, I've made -some changes to the codebase that cause some of the text above to no -longer be current. I've left the text intact, but added markers at the -passages that are now inaccurate. The new situation is described -below.

-
-
-

Content Representation

- -

The original implementation of CodeMirror 2 represented the -document as a flat array of line objects. This worked well—splicing -arrays will require the part of the array after the splice to be -moved, but this is basically just a simple memmove of a -bunch of pointers, so it is cheap even for huge documents.

- -

However, I recently added line wrapping and code folding (line -collapsing, basically). Once lines start taking up a non-constant -amount of vertical space, looking up a line by vertical position -(which is needed when someone clicks the document, and to determine -the visible part of the document during scrolling) can only be done -with a linear scan through the whole array, summing up line heights as -you go. Seeing how I've been going out of my way to make big documents -fast, this is not acceptable.

- -

The new representation is based on a B-tree. The leaves of the tree -contain arrays of line objects, with a fixed minimum and maximum size, -and the non-leaf nodes simply hold arrays of child nodes. Each node -stores both the amount of lines that live below them and the vertical -space taken up by these lines. This allows the tree to be indexed both -by line number and by vertical position, and all access has -logarithmic complexity in relation to the document size.

- -

I gave line objects and tree nodes parent pointers, to the node -above them. When a line has to update its height, it can simply walk -these pointers to the top of the tree, adding or subtracting the -difference in height from each node it encounters. The parent pointers -also make it cheaper (in complexity terms, the difference is probably -tiny in normal-sized documents) to find the current line number when -given a line object. In the old approach, the whole document array had -to be searched. Now, we can just walk up the tree and count the sizes -of the nodes coming before us at each level.

- -

I chose B-trees, not regular binary trees, mostly because they -allow for very fast bulk insertions and deletions. When there is a big -change to a document, it typically involves adding, deleting, or -replacing a chunk of subsequent lines. In a regular balanced tree, all -these inserts or deletes would have to be done separately, which could -be really expensive. In a B-tree, to insert a chunk, you just walk -down the tree once to find where it should go, insert them all in one -shot, and then break up the node if needed. This breaking up might -involve breaking up nodes further up, but only requires a single pass -back up the tree. For deletion, I'm somewhat lax in keeping things -balanced—I just collapse nodes into a leaf when their child count goes -below a given number. This means that there are some weird editing -patterns that may result in a seriously unbalanced tree, but even such -an unbalanced tree will perform well, unless you spend a day making -strangely repeating edits to a really big document.

-
-
-

Keymaps

- -

Above, I claimed that directly catching key -events for things like cursor movement is impractical because it -requires some browser-specific kludges. I then proceeded to explain -some awful hacks that were needed to make it -possible for the selection changes to be detected through the -textarea. In fact, the second hack is about as bad as the first.

- -

On top of that, in the presence of user-configurable tab sizes and -collapsed and wrapped lines, lining up cursor movement in the textarea -with what's visible on the screen becomes a nightmare. Thus, I've -decided to move to a model where the textarea's selection is no longer -depended on.

- -

So I moved to a model where all cursor movement is handled by my -own code. This adds support for a goal column, proper interaction of -cursor movement with collapsed lines, and makes it possible for -vertical movement to move through wrapped lines properly, instead of -just treating them like non-wrapped lines.

- -

The key event handlers now translate the key event into a string, -something like Ctrl-Home or Shift-Cmd-R, and -use that string to look up an action to perform. To make keybinding -customizable, this lookup goes through -a table, using a scheme that -allows such tables to be chained together (for example, the default -Mac bindings fall through to a table named 'emacsy', which defines -basic Emacs-style bindings like Ctrl-F, and which is also -used by the custom Emacs bindings).

- -

A new -option extraKeys -allows ad-hoc keybindings to be defined in a much nicer way than what -was possible with the -old onKeyEvent -callback. You simply provide an object mapping key identifiers to -functions, instead of painstakingly looking at raw key events.

- -

Built-in commands map to strings, rather than functions, for -example "goLineUp" is the default action bound to the up -arrow key. This allows new keymaps to refer to them without -duplicating any code. New commands can be defined by assigning to -the CodeMirror.commands object, which maps such commands -to functions.

- -

The hidden textarea now only holds the current selection, with no -extra characters around it. This has a nice advantage: polling for -input becomes much, much faster. If there's a big selection, this text -does not have to be read from the textarea every time—when we poll, -just noticing that something is still selected is enough to tell us -that no new text was typed.

- -

The reason that cheap polling is important is that many browsers do -not fire useful events on IME (input method engine) input, which is -the thing where people inputting a language like Japanese or Chinese -use multiple keystrokes to create a character or sequence of -characters. Most modern browsers fire input when the -composing is finished, but many don't fire anything when the character -is updated during composition. So we poll, whenever the -editor is focused, to provide immediate updates of the display.

- -
-
diff --git a/CODE/js/codemirror/doc/logo.png b/CODE/js/codemirror/doc/logo.png deleted file mode 100644 index 9aabda1d709f565bb115fa083cc16b6d52db64cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9310 zcmWk!Wl$VV6g=D|1b25!aEIUyhr7GGTL{5|1$PMU!Gb&FaCaxTyW6*4U2W~w{MehF z_g=s5o{3aZl14)&LC@FTctEwWVDw?~{3&X8ah!PRD9*tQT8757ES=R=O zZIdr2ugtx4l6@a%-%CqPVF44eaQ%2fx0Jpla6f)NKFVjM^Z_h)hKuzjB4sSI@ekEg z@U#NrAp>;|c$0x9hg?WZTdZG(0VD4>#GX6z>|wuiM$CS;G$FUC%`Yu#T3S`2xx9Q% z&L>hk-l%{+T9H<*SA=}sbo%%o@Lho^HR{1J(LU%?j7M6P@vF0Z0pn zRg3c$bb5L^PunUDMRKYs2Bi84p?^h2s-wqmdrz@zp~k4I#jPdj=S+$6^$1(n#c@vV zQV@AYWo0F0iuwG?$(^c;iwoWx)a709!-19&TMi)>)^tO&r)EX+#@$=$u6%0kebYPc zJhdwF-;A+6Nv~cvrd5Mk9vYf~d&?L>K|$|Ad8<9vw25-A3Z(NDgczyq)xxNFX3irt z5&W3H!9k+!9`(QJE||;POE|miR+~Sm6)TtkQ-cj}VANhC(QrgTL%V&q;;Zro-FDY} zLEOj{mjZVGgd>u4m z9zF7e&^@tyDqDe64B@zwmmb{Zn8=9D0EkzmV(u^0Er>Me3Q1W@zsY6$umA0>sVZm? zf(GAr5tO2~04nkJ@wvUbz0JP5-v~N(8$pml|2M|7;cjEJKc2naVlq*V$ErUVu%xCc zfs#1n{*MfE1l}<8>Gn8IRz?OtB`Ikq$e{Y8Acr;iHY83OaqB8@B8#tYMp?M1xTHiN zI7wa+cO)-Jl$p=D%D=t+`FM2=o|c+=mO*=?NKfnSwkBw*RJ8!PBhEop&nIA&$mb;v z`(VuSI$`*;(y|?=VesS!rUgt{Jdt*)`0hZ?G z7usFYr+DK=vmmf4*I#j$aOU?KF(7dFFyg^IqOV_XZ;56L^zPYvdwU(1ez>LJ;p|HN z4VWobRPeX^YQ|=%QMp8X`Hn78u&x4ka1N?5=y}S^Blo>WDpAfA7QE<&X|fPi*P!91 zP1%g!{BoS*|03)2@?Zo#sF~owf;%$sj_3DF)yW$ZALRySHTxJ?p2Ot=fq#BIgX)5|XO;LcR_79G0BHnqmaV{@z-l*0V&*FitQ+$n$GKK~(Sm8!dKL(9qB_GBTi@yf$25rWcs(1P9>X)ru?O`26nlrz=f0 zm@U<-rU~O=l$e1aj>uh@$@ff@7IHbdSS0=;!NwcMhud z53gSK=5Ft3eehCzt=I&`DimK+)jCpbx;}m$z19HbQtn`d)!hQVW?ChL; zbyKo%9MIsH(Q>--r@-OVua@MI!wIa-jBE9Yw#B6Hdl@SfkOB%&hU2fK`1pfgB~cEi z>K7acZUFF41c0jUSTU1(EXAar0TlOu9ixo!E{YZ*$|c}u>+__hBK&*hRIDa8Af}Q; zWDit2MeZN4SZfyUHj}GWt#8A|6Cu@MLU~bqjhe+}#m%DI(CommzwUQ#@%IP8XxgXH zkyN(SV_iLNCzH9TWd7jqj-)r0;Kso}!idX{4~*c?b&}V(1|!o(&NG`&2I&`ADZUIJ|pBJ69gpMjxo{`=IqPIR49;HZJ)o;WIDAx4o8- z1j5nrhNlg>pzELyfsea#kffV~rD%Ax^X=p*GhV+t=9NEE zwGMXJptrZT*>W|aY^89`irs&YC|M#w!f}@{7kC*bUPqjh!Uwy0bsC~_hGjab`^uy} ztltpI_08Dvm(#}fwt2E&myYl$E+P(Q!cUqD3UxVZSM03`xg;(xj3F4~&-sfCMd~A{ zKqcWr3+}cf1UzhoH@&vk(EDzNAZT}ZqK%}mY#-Nj(O&AtR+xan;967_CN{F;l|~|8 zbv4eSqnd;^x`NQ7i^pBJQWXuEPbA(AiyNG_JNvs^k2SQ48Kr?Gew5?MKFwqKY(F(DdQVId#=h;KNtK-Z_T*+|&{hTo_Dmm~TmWj5{v?&Y`#Q-Eb zTz&kT>byP|t@F$QR+V?o4xQ)xp3Ohpq{U`hSM0{j5y}#&wUnJ=k1^XyQfGGV`7zm0 zE56Y;?0&vKmsggnf@JGdGPs5f1=AO!H`mBk5|vl*z}R z*lpQn=>%|W7C2x6OuNW6mFZ4#aq$jO;SZRY@s{oHf#++f@5Odiy7Y#IuOBOL;sdb5rDr#wo^`On0`NwfaORizU;5S`T|a`{+m2KhV!OF2ne zn)5u+*B7c3=iXT%Iyyc+&gM;Z1*+9%ehzt68mX~f`BMd1@9^Uoo+;_u?+Wkg3K|X| zjHx*~QxY;V!iLQ-hH3LW{Wn{t=CjfuRg$i^ESx3gzuNLkZD0E0=BB+uR50L5z2_I@ z0CYH%y42$a@fVdzp$8SB%r$@50Z3+R&bl?>tn~Idgp!Uc6LT~!GE$bg@oDPt9dgA| z)Zx3K0a~`_w(`&$?;ANGV>IlkAOtPLHeJ=_4)Imh6kAU=Ael_$2x%+q9s8$9adapc z@ZR;|^71uWa}qB9aq`j%@do6JI#;g5gO~kkLO@fV9hIo_EwQ|kO+AI6P%VPG$x%=- z@5C!jI64WxmL(<%YY`eDXQg`aZFOF5&|6FJU=Oa3e0$`#p1wY+f@)TXyJT_UFBF@h zm&fbunEXn@wzIX*%ASx7e`1A#zcI}#*00OAaA}0x8_-MYP#IkIi|jhGU7Z~{*Y~X5 z3Bz%wvHe+$Y!UGLy#h-+zuEcix%UM~Ky`&lIGtqNPV4$8-t%@U8wSgDQ_X&PC>YzX z3~E{1JN?fOkI56WeWg6J@KEE%i6B9%?8D(U|25<-TDdbvw=dRGJ=x+Pzw*;ZBPnm%Q1e{g)fvam7XWvnY_ z`y0C)y*qsiC%B2!CtHWE!`L|+|AvSX;yDQyy+nYrR3nwpYQ zJZH@bm2-AR0s{?ADlac$ zkx01)laBkr=%@l^j&DXHo$?;5U6S;#=JU>H{Ll9EfOHzrZG8vU#$Rw({7!P;`l4{N zG@f%v$3a!h-uc>@$S^BZL8p1fpBpb(buJ}6eJJIeClz0mfySxp)sr;X(9p1*+mZQx z!-#)MRuy}>As`~X-m2E~m7g@|Z z=1NnAGf&Ig$rmUc%eOqeCP(*^Mb*c>Os6!yZIt|SaB{C9#pri-&IJgJk7N6HT&Wah z9k2~ZFz|&pG>2etaRG99GsCkF10?k5H=YgX&BM13?U#Rv!fbO-JOx}*c;EOh@qo|r z_Ve35Jynb$;s zfuT&*mrEAvtLK5Y$;_@8VTK)M0G9rk15iwC*`mH_Y zTr=PfTv5ukYR1Rc+8hNU>Mh4i*R!&&r!1IK@N6nQwpM*`S>B?G6*I9&NzcFo9czwO z+kaz5b9qwTtS0c5r3$9l*|Ip zP^-oekWvM_v054$w@z9d+*6_vZd-3KQ%$?{!;VcH9!Kh$%pCUVqFY}MjCIzeeTz#*Hqqwvp81OK z>w5Tar|?+{)Wdf5+Gqw>w8+~-A3=L)=`d4m%3^!p;98rP6--6QoNvdQA%%n&g@t^# zv?^zShDMby`x6Kd;J*Q$ddy?Eu)?a{5OH(kUWM&y!3RQx>x5e!j9k36&nKs)rb*V7 z)*tK&p$qH(8g{oIsgfK-Ay%0yB$zKClk&(7fSgmtC+!^t&zphF<=^r zfXnT$+)%DLg|4o_l^Z!y6#b|*7LCUyC#i}xW~t`$c_3g1#GXpZgxxO^OImgjKMJ^H zmAZ{lZ2Mzgux)N&UT>c(K)z^Lzdzrdn(h5y<8?n!>49^J`K9qU>96{ik|{+vzF)>Z z#*Hte5ISXIWgtS3I$dl)FX)PvKo8v8g{Z+1L5QP@lsBZA%r7c6nV2*yjW2)IH2dD# z<~!6?rt7ymcVe1tTyZeBd7bWLI(1R)b@zOIg1CA1pjg%)K7-GTzPwRW^8`QiMpIqC z#Vxl==J5M0*HfRZ45$P-az3C>LU-ZX`_S*@jr5ER4i3VcFE`rpkes|L)bIQnjfWEVseduN~aJG0f7S$^cFb@LBvA6KZ`~dpI%eGT+_{$sb$t%P3(UDByT#lH|WYvjN*cf z0b`D{*oIX|&b=(W1r zq&JVJ8U?0r+~GQsY#yJAzl%+t2IhB1iR0GGTTlUx9YO?z zAu95t;&6Z;IzQoVSbIa-C^2`YjU#(fc^`)GL!Jug_2mVhllLc(c$sI7RMN53%*;Q0 zuJ>yF_F1QA8YuB;<+S$H+w=aFE1GqWv-6dM6UO1yLjF9Q1{2P1}rE3j*P*0 zIVlSY36*KpkV=K-?D_U7D!(UUVI52t+2xpS%_x^lHTbBJp5JSeUQ@v)0*N}L4*mHE zEvFkP&(fq7i+qxxpM3KNhjh`Db;Og2UM;f0zL5Jv{lsod+@U)^CH#%m&R_y8ta|m2 zYQXBXI=osepS; zW2_+-c5}5$jVv_v1(TM&0^HglgzAQI>w21-jw`3oc#pYtZj@9xB8BCd@u z@Wngwe$o@ezzdf@fR+wc%iNrL0^KVGv%iv?f?{Ib60{b=6IJ>LDz`T#1kV!caeBDQ zuq6u5&^upyC6mKy+de=Tj*`R?) z(kA$|$GF>?bDh)pXFZf_@hSgBP3IFM(Az5wTaX!w1_wuV0ZCD^@G~(H(%;{|8Y@1( z@61ojEV_}wyvM;4Y+aZ3ZZ}I5NCUCswZf24b4epXE?H@-NM zHNT2_@l_*jG#{IUTs~6Q58po2Ea+KWNEtwD{`cLk+VPw^ZgDr9mb2nU0$gU_F$oAF zHTCYOp>Y!<2M2pf0s9NuNtsQvN>3c1!O{9?uBk>E8aep`jd?G0K{BiN&;glP)!{V|1@J_|hJ&i%FZv@t`iu~WCNpYixA@}peMe)$3kJHL zLK-iHvItWVHJ4D#f}vlPOg;AgtJ_9rK&4(=acTBlb{%{z3n-6`y(^8@E*pv&OCoU~ z3wG6*i0|h#`a#7K$5OdY zijMc=q<&g*+<5S+cd<776F!~5CbT=Dqu>?e&lnD! z(G94TE2LprS-{rGMuubO<4ad0;@%55pRTgYx4Dp$|Gzsg{VW=H-K1l*wUSKc0QmqR z$su4@Ia~KBAR8{35!pSMqUt?t(!pb_<Ww zfah;*u-#flq}%R!tb6?#V;`Au<>vt&jL&cYeWmW|TKE5+1eMf}L96O5?Bka@Z1-5M z%sz|(k|&l8L%Z6S@*Yw{bWkRUDO$Cr1DIJ5n>$5UdQq^dlQq%_+?AjMu!Z*UwBlq9 zl*bV7*ek-|+bL)XL9tZwL?~mqanj?w{~O85VyBx`4(h{7=f;Q!d%MLoE?}Y2*Vore zj>FUsVp|cYPD=ceO{DE~)NR5bvBc#M_PsrtVQj#0N$Ep}VdOIkOC=%guEwUbg?=xJ zZ)iu?wW>?Yr3as}M7D&hIz>=JdKt;YuXlUl2B2-aoD+WVMgJya)d}{%IdJ>O&VUZp zu38{fsnhglG_)S*%8!pX6InT`{$|4Vfq$CgNAqLIN-f;nf@I|=qZc!=M4532FAV{$ zN`nVFfH{HE#TnR#sv`E6TM~13zvJVBdXa-Id|maKrVR{MU}&-Px0hmM8!!@SKU+Ch zrB!C_Abonc+*ZgDMGJVV#GJUM$TtIRpd=dS_-4-2BiKwnDn#b5>v&tpX z+AB6&%40nwtEH|!`;R#sFZg_0@pIMA0D>}r1)bRUL!VA1!$C@>_lm3!W%A?-AB&Xm)~uFywT%$n#=8W zSc4QS2PcF5l_!JKZn>6P^~$N%R1aX07$IAdoU(Afnaq1wHX(A^zt&25yg|nd@i)^x zHj23&m@DH#MXhJimqE_F+fQ3gRl1EpTUt>JyWbs29RXB@d2vaxk3JdbvPG)ov0p>@ zk`;cE=mDNSO0@<4=CjTy4Z4oz(F-DN^pc!X`E6C$*dUqLsZ`46@R`5;c8?&~gLSG( z?T#bBLq|kGsSkL%r0a#s!`b1i%B;>GGR;tzg{nk(0iy=v1;-Xa{Z`R*o54ryh)BRaz z5J?M`5b%%LpmMR>W#POLL;o^>KrkEsp9SF0-QH7?$QrD>3h1@?Lt{WyhUvn> z&K{4J$-V9@)oe9ED%EvUmjvdw%!^3)a`!oNO!Dao@JqYcXOdE5 zP~Xs{v8`s31Z?}sq-tuke#*lT#l*Mx?Y7jopM}}thh+oM+9gUPdjWj!@E|D9)8#OZ zV9-*$YhO1(Og11yY8g!l`2PNZbCCNgDjdcb#DR~x&4}1;T(JH-F-s*&5(*#gzyQkF zChYYmC>>VN;NeR5;m7^$ZP7aijQ3U;Ues(tjz~~vo)HYPX+ZQIiu#**o zL!i(O2=(@h-sqdNnYP8gb-?Gx`(yebn_n#fr(qrCD+?ypH)5%_B1HTNeoDv46gar` z<-rTyM`(v@2M>-;KvJP50NSBeP3ojN^&u(7R~!!^O_ISI2^slcx)_YlQCSHxm+$cC zXrbSN@KF#`XZ_8wjuK;H8}<)^djB7KXf{L8ZE{pRp0B#Lpcoq* z)Ltp}I5x)&b@}|y^%qsCX#>M}LqX{=kKOzUY4)T?Lht&pdF>CDJ~wY(Y?2A+HK$%R zeOO_bJ)oH*qsh9;i!$et<0~rJc4Zdx)?nd_0vk>F`S~q*o=!`3MLHo+rTAgpdXm)g z6a}kmYdn(D(kqOcIYiI~B)OzC&F5XjFWsg9IQcOBy-oRgz{&o$WqhRTkk?NFCCr^lDm1lJD@v#Qli~ZnEZ$Xji^p29BteJ3I(;JX2>ye%XjYfT!8qfs zA%}?qr2)g~1wkjqECv7o diff --git a/CODE/js/codemirror/doc/logo.svg b/CODE/js/codemirror/doc/logo.svg deleted file mode 100644 index b39b24c8..00000000 --- a/CODE/js/codemirror/doc/logo.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/CODE/js/codemirror/doc/manual.html b/CODE/js/codemirror/doc/manual.html deleted file mode 100644 index 89a6328e..00000000 --- a/CODE/js/codemirror/doc/manual.html +++ /dev/null @@ -1,3749 +0,0 @@ - - -CodeMirror: User Manual - - - - - - - - - - - - - - - - -
- -
-

- User manual and reference guide - version 5.58.3 -

- -

CodeMirror is a code-editor component that can be embedded in - Web pages. The core library provides only the editor - component, no accompanying buttons, auto-completion, or other IDE - functionality. It does provide a rich API on top of which such - functionality can be straightforwardly implemented. See - the addons included in the distribution, - and 3rd party - packages on npm, for reusable implementations of extra - features.

- -

CodeMirror works with language-specific modes. Modes are - JavaScript programs that help color (and optionally indent) text - written in a given language. The distribution comes with a number - of modes (see the mode/ - directory), and it isn't hard to write new - ones for other languages.

-
- -
-

Basic Usage

- -

The easiest way to use CodeMirror is to simply load the script - and style sheet found under lib/ in the distribution, - plus a mode script from one of the mode/ directories. - For example:

- -
<script src="lib/codemirror.js"></script>
-<link rel="stylesheet" href="lib/codemirror.css">
-<script src="mode/javascript/javascript.js"></script>
- -

(Alternatively, use a module loader. More - about that later.)

- -

Having done this, an editor instance can be created like - this:

- -
var myCodeMirror = CodeMirror(document.body);
- -

The editor will be appended to the document body, will start - empty, and will use the mode that we loaded. To have more control - over the new editor, a configuration object can be passed - to CodeMirror as a second - argument:

- -
var myCodeMirror = CodeMirror(document.body, {
-  value: "function myScript(){return 100;}\n",
-  mode:  "javascript"
-});
- -

This will initialize the editor with a piece of code already in - it, and explicitly tell it to use the JavaScript mode (which is - useful when multiple modes are loaded). - See below for a full discussion of the - configuration options that CodeMirror accepts.

- -

In cases where you don't want to append the editor to an - element, and need more control over the way it is inserted, the - first argument to the CodeMirror function can also - be a function that, when given a DOM element, inserts it into the - document somewhere. This could be used to, for example, replace a - textarea with a real editor:

- -
var myCodeMirror = CodeMirror(function(elt) {
-  myTextArea.parentNode.replaceChild(elt, myTextArea);
-}, {value: myTextArea.value});
- -

However, for this use case, which is a common way to use - CodeMirror, the library provides a much more powerful - shortcut:

- -
var myCodeMirror = CodeMirror.fromTextArea(myTextArea);
- -

This will, among other things, ensure that the textarea's value - is updated with the editor's contents when the form (if it is part - of a form) is submitted. See the API - reference for a full description of this method.

- -

Module loaders

- -

The files in the CodeMirror distribution contain shims for - loading them (and their dependencies) in AMD or CommonJS - environments. If the variables exports - and module exist and have type object, CommonJS-style - require will be used. If not, but there is a - function define with an amd property - present, AMD-style (RequireJS) will be used.

- -

It is possible to - use Browserify or similar - tools to statically build modules using CodeMirror. Alternatively, - use RequireJS to dynamically - load dependencies at runtime. Both of these approaches have the - advantage that they don't use the global namespace and can, thus, - do things like load multiple versions of CodeMirror alongside each - other.

- -

Here's a simple example of using RequireJS to load CodeMirror:

- -
require([
-  "cm/lib/codemirror", "cm/mode/htmlmixed/htmlmixed"
-], function(CodeMirror) {
-  CodeMirror.fromTextArea(document.getElementById("code"), {
-    lineNumbers: true,
-    mode: "htmlmixed"
-  });
-});
- -

It will automatically load the modes that the mixed HTML mode - depends on (XML, JavaScript, and CSS). Do not use - RequireJS' paths option to configure the path to - CodeMirror, since it will break loading submodules through - relative paths. Use - the packages - configuration option instead, as in:

- -
require.config({
-  packages: [{
-    name: "codemirror",
-    location: "../path/to/codemirror",
-    main: "lib/codemirror"
-  }]
-});
- -
- -
-

Configuration

- -

Both the CodeMirror - function and its fromTextArea method take as second - (optional) argument an object containing configuration options. - Any option not supplied like this will be taken - from CodeMirror.defaults, an - object containing the default options. You can update this object - to change the defaults on your page.

- -

Options are not checked in any way, so setting bogus option - values is bound to lead to odd errors.

- -

These are the supported options:

- -
-
value: string|CodeMirror.Doc
-
The starting value of the editor. Can be a string, or - a document object.
- -
mode: string|object
-
The mode to use. When not given, this will default to the - first mode that was loaded. It may be a string, which either - simply names the mode or is - a MIME type - associated with the mode. The value "null" - indicates no highlighting should be applied. Alternatively, it - may be an object containing configuration options for the mode, - with a name property that names the mode (for - example {name: "javascript", json: true}). The demo - pages for each mode contain information about what configuration - parameters the mode supports. You can ask CodeMirror which modes - and MIME types have been defined by inspecting - the CodeMirror.modes - and CodeMirror.mimeModes objects. The first maps - mode names to their constructors, and the second maps MIME types - to mode specs.
- -
lineSeparator: string|null
-
Explicitly set the line separator for the editor. By default - (value null), the document will be split on CRLFs - as well as lone CRs and LFs, and a single LF will be used as - line separator in all output (such - as getValue). When a - specific string is given, lines will only be split on that - string, and output will, by default, use that same - separator.
- -
theme: string
-
The theme to style the editor with. You must make sure the - CSS file defining the corresponding .cm-s-[name] - styles is loaded (see - the theme directory in the - distribution). The default is "default", for which - colors are included in codemirror.css. It is - possible to use multiple theming classes at once—for - example "foo bar" will assign both - the cm-s-foo and the cm-s-bar classes - to the editor.
- -
indentUnit: integer
-
How many spaces a block (whatever that means in the edited - language) should be indented. The default is 2.
- -
smartIndent: boolean
-
Whether to use the context-sensitive indentation that the - mode provides (or just indent the same as the line before). - Defaults to true.
- -
tabSize: integer
-
The width of a tab character. Defaults to 4.
- -
indentWithTabs: boolean
-
Whether, when indenting, the first N*tabSize - spaces should be replaced by N tabs. Default is false.
- -
electricChars: boolean
-
Configures whether the editor should re-indent the current - line when a character is typed that might change its proper - indentation (only works if the mode supports indentation). - Default is true.
- -
specialChars: RegExp
-
A regular expression used to determine which characters - should be replaced by a - special placeholder. - Mostly useful for non-printing special characters. The default - is /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/.
-
specialCharPlaceholder: function(char) → Element
-
A function that, given a special character identified by - the specialChars - option, produces a DOM node that is used to represent the - character. By default, a red dot () - is shown, with a title tooltip to indicate the character code.
- -
direction: "ltr" | "rtl"
-
Flips overall layout and selects base paragraph direction to - be left-to-right or right-to-left. Default is "ltr". - CodeMirror applies the Unicode Bidirectional Algorithm to each - line, but does not autodetect base direction — it's set to the - editor direction for all lines. The resulting order is - sometimes wrong when base direction doesn't match user intent - (for example, leading and trailing punctuation jumps to the - wrong side of the line). Therefore, it's helpful for - multilingual input to let users toggle this option. - -
rtlMoveVisually: boolean
-
Determines whether horizontal cursor movement through - right-to-left (Arabic, Hebrew) text is visual (pressing the left - arrow moves the cursor left) or logical (pressing the left arrow - moves to the next lower index in the string, which is visually - right in right-to-left text). The default is false - on Windows, and true on other platforms.
- -
keyMap: string
-
Configures the key map to use. The default - is "default", which is the only key map defined - in codemirror.js itself. Extra key maps are found in - the key map directory. See - the section on key maps for more - information.
- -
extraKeys: object
-
Can be used to specify extra key bindings for the editor, - alongside the ones defined - by keyMap. Should be - either null, or a valid key map value.
- -
configureMouse: fn(cm: CodeMirror, repeat: "single" | "double" | "triple", event: Event) → Object
-
Allows you to configure the behavior of mouse selection and - dragging. The function is called when the left mouse button is - pressed. The returned object may have the following properties: -
-
unit: "char" | "word" | "line" | "rectangle" | fn(CodeMirror, Pos) → {from: Pos, to: Pos}
-
The unit by which to select. May be one of the built-in - units or a function that takes a position and returns a - range around that, for a custom unit. The default is to - return "word" for double - clicks, "line" for triple - clicks, "rectangle" for alt-clicks (or, on - Chrome OS, meta-shift-clicks), and "single" - otherwise.
-
extend: bool
-
Whether to extend the existing selection range or start - a new one. By default, this is enabled when shift - clicking.
-
addNew: bool
-
When enabled, this adds a new range to the existing - selection, rather than replacing it. The default behavior is - to enable this for command-click on Mac OS, and - control-click on other platforms.
-
moveOnDrag: bool
-
When the mouse even drags content around inside the - editor, this controls whether it is copied (false) or moved - (true). By default, this is enabled by alt-clicking on Mac - OS, and ctrl-clicking elsewhere.
-
-
- -
lineWrapping: boolean
-
Whether CodeMirror should scroll or wrap for long lines. - Defaults to false (scroll).
- -
lineNumbers: boolean
-
Whether to show line numbers to the left of the editor.
- -
firstLineNumber: integer
-
At which number to start counting lines. Default is 1.
- -
lineNumberFormatter: function(line: integer) → string
-
A function used to format line numbers. The function is - passed the line number, and should return a string that will be - shown in the gutter.
- -
gutters: array<string | {className: string, style: ?string}>
-
Can be used to add extra gutters (beyond or instead of the - line number gutter). Should be an array of CSS class names or - class name / CSS string pairs, each of which defines - a width (and optionally a background), and which - will be used to draw the background of the gutters. May - include the CodeMirror-linenumbers class, in order - to explicitly set the position of the line number gutter (it - will default to be to the right of all other gutters). These - class names are the keys passed - to setGutterMarker.
- -
fixedGutter: boolean
-
Determines whether the gutter scrolls along with the content - horizontally (false) or whether it stays fixed during horizontal - scrolling (true, the default).
- -
scrollbarStyle: string
-
Chooses a scrollbar implementation. The default - is "native", showing native scrollbars. The core - library also provides the "null" style, which - completely hides the - scrollbars. Addons can - implement additional scrollbar models.
- -
coverGutterNextToScrollbar: boolean
-
When fixedGutter - is on, and there is a horizontal scrollbar, by default the - gutter will be visible to the left of this scrollbar. If this - option is set to true, it will be covered by an element with - class CodeMirror-gutter-filler.
- -
inputStyle: string
-
Selects the way CodeMirror handles input and focus. The core - library defines the "textarea" - and "contenteditable" input models. On mobile - browsers, the default is "contenteditable". On - desktop browsers, the default is "textarea". - Support for IME and screen readers is better in - the "contenteditable" model. The intention is to - make it the default on modern desktop browsers in the - future.
- -
readOnly: boolean|string
-
This disables editing of the editor content by the user. If - the special value "nocursor" is given (instead of - simply true), focusing of the editor is also - disallowed.
- -
screenReaderLabel: string
-
This label is read by the screenreaders when CodeMirror text area is focused. This - is helpful for accessibility.
- -
showCursorWhenSelecting: boolean
-
Whether the cursor should be drawn when a selection is - active. Defaults to false.
- -
lineWiseCopyCut: boolean
-
When enabled, which is the default, doing copy or cut when - there is no selection will copy or cut the whole lines that have - cursors on them.
- -
pasteLinesPerSelection: boolean
-
When pasting something from an external source (not from the - editor itself), if the number of lines matches the number of - selection, CodeMirror will by default insert one line per - selection. You can set this to false to disable - that behavior.
- -
selectionsMayTouch: boolean
-
Determines whether multiple selections are joined as soon as - they touch (the default) or only when they overlap (true).
- -
undoDepth: integer
-
The maximum number of undo levels that the editor stores. - Note that this includes selection change events. Defaults to - 200.
- -
historyEventDelay: integer
-
The period of inactivity (in milliseconds) that will cause a - new history event to be started when typing or deleting. - Defaults to 1250.
- -
tabindex: integer
-
The tab - index to assign to the editor. If not given, no tab index - will be assigned.
- -
autofocus: boolean
-
Can be used to make CodeMirror focus itself on - initialization. Defaults to off. - When fromTextArea is - used, and no explicit value is given for this option, it will be - set to true when either the source textarea is focused, or it - has an autofocus attribute and no other element is - focused.
- -
phrases: ?object
-
Some addons run user-visible strings (such as labels in the - interface) through the phrase - method to allow for translation. This option determines the - return value of that method. When it is null or an object that - doesn't have a property named by the input string, that string - is returned. Otherwise, the value of the property corresponding - to that string is returned.
-
- -

Below this a few more specialized, low-level options are - listed. These are only useful in very specific situations, you - might want to skip them the first time you read this manual.

- -
-
dragDrop: boolean
-
Controls whether drag-and-drop is enabled. On by default.
- -
allowDropFileTypes: array<string>
-
When set (default is null) only files whose - type is in the array can be dropped into the editor. The strings - should be MIME types, and will be checked against - the type - of the File object as reported by the browser.
- -
cursorBlinkRate: number
-
Half-period in milliseconds used for cursor blinking. The default blink - rate is 530ms. By setting this to zero, blinking can be disabled. A - negative value hides the cursor entirely.
- -
cursorScrollMargin: number
-
How much extra space to always keep above and below the - cursor when approaching the top or bottom of the visible view in - a scrollable document. Default is 0.
- -
cursorHeight: number
-
Determines the height of the cursor. Default is 1, meaning - it spans the whole height of the line. For some fonts (and by - some tastes) a smaller height (for example 0.85), - which causes the cursor to not reach all the way to the bottom - of the line, looks better
- -
resetSelectionOnContextMenu: boolean
-
Controls whether, when the context menu is opened with a - click outside of the current selection, the cursor is moved to - the point of the click. Defaults to true.
- -
workTime, workDelay: number
-
Highlighting is done by a pseudo background-thread that will - work for workTime milliseconds, and then use - timeout to sleep for workDelay milliseconds. The - defaults are 200 and 300, you can change these options to make - the highlighting more or less aggressive.
- -
pollInterval: number
-
Indicates how quickly CodeMirror should poll its input - textarea for changes (when focused). Most input is captured by - events, but some things, like IME input on some browsers, don't - generate events that allow CodeMirror to properly detect it. - Thus, it polls. Default is 100 milliseconds.
- -
flattenSpans: boolean
-
By default, CodeMirror will combine adjacent tokens into a - single span if they have the same class. This will result in a - simpler DOM tree, and thus perform better. With some kinds of - styling (such as rounded corners), this will change the way the - document looks. You can set this option to false to disable this - behavior.
- -
addModeClass: boolean
-
When enabled (off by default), an extra CSS class will be - added to each token, indicating the - (inner) mode that produced it, prefixed - with "cm-m-". For example, tokens from the XML mode - will get the cm-m-xml class.
- -
maxHighlightLength: number
-
When highlighting long lines, in order to stay responsive, - the editor will give up and simply style the rest of the line as - plain text when it reaches a certain position. The default is - 10 000. You can set this to Infinity to turn off - this behavior.
- -
viewportMargin: integer
-
Specifies the amount of lines that are rendered above and - below the part of the document that's currently scrolled into - view. This affects the amount of updates needed when scrolling, - and the amount of work that such an update does. You should - usually leave it at its default, 10. Can be set - to Infinity to make sure the whole document is - always rendered, and thus the browser's text search works on it. - This will have bad effects on performance of big - documents.
- -
spellcheck: boolean
-
Specifies whether or not spellcheck will be enabled on the input.
- -
autocorrect: boolean
-
Specifies whether or not autocorrect will be enabled on the input.
- -
autocapitalize: boolean
-
Specifies whether or not autocapitalization will be enabled on the input.
-
-
- -
-

Events

- -

Various CodeMirror-related objects emit events, which allow - client code to react to various situations. Handlers for such - events can be registered with the on - and off methods on the objects - that the event fires on. To fire your own events, - use CodeMirror.signal(target, name, args...), - where target is a non-DOM-node object.

- -

An editor instance fires the following events. - The instance argument always refers to the editor - itself.

- -
-
"change" (instance: CodeMirror, changeObj: object)
-
Fires every time the content of the editor is changed. - The changeObj is a {from, to, text, removed, - origin} object containing information about the changes - that occurred as second argument. from - and to are the positions (in the pre-change - coordinate system) where the change started and ended (for - example, it might be {ch:0, line:18} if the - position is at the beginning of line #19). text is - an array of strings representing the text that replaced the - changed range (split by line). removed is the text - that used to be between from and to, - which is overwritten by this change. This event is - fired before the end of - an operation, before the DOM updates - happen.
- -
"changes" (instance: CodeMirror, changes: array<object>)
-
Like the "change" - event, but batched per operation, - passing an array containing all the changes that happened in the - operation. This event is fired after the operation finished, and - display changes it makes will trigger a new operation.
- -
"beforeChange" (instance: CodeMirror, changeObj: object)
-
This event is fired before a change is applied, and its - handler may choose to modify or cancel the change. - The changeObj object - has from, to, and text - properties, as with - the "change" event. It - also has a cancel() method, which can be called to - cancel the change, and, if the change isn't - coming from an undo or redo event, an update(from, to, - text) method, which may be used to modify the change. - Undo or redo changes can't be modified, because they hold some - metainformation for restoring old marked ranges that is only - valid for that specific change. All three arguments - to update are optional, and can be left off to - leave the existing value for that field - intact. Note: you may not do anything from - a "beforeChange" handler that would cause changes - to the document or its visualization. Doing so will, since this - handler is called directly from the bowels of the CodeMirror - implementation, probably cause the editor to become - corrupted.
- -
"cursorActivity" (instance: CodeMirror)
-
Will be fired when the cursor or selection moves, or any - change is made to the editor content.
- -
"keyHandled" (instance: CodeMirror, name: string, event: Event)
-
Fired after a key is handled through a - key map. name is the name of the handled key (for - example "Ctrl-X" or "'q'"), - and event is the DOM keydown - or keypress event.
- -
"inputRead" (instance: CodeMirror, changeObj: object)
-
Fired whenever new input is read from the hidden textarea - (typed or pasted by the user).
- -
"electricInput" (instance: CodeMirror, line: integer)
-
Fired if text input matched the - mode's electric patterns, - and this caused the line's indentation to change.
- -
"beforeSelectionChange" (instance: CodeMirror, obj: {ranges, origin, update})
-
This event is fired before the selection is moved. Its - handler may inspect the set of selection ranges, present as an - array of {anchor, head} objects in - the ranges property of the obj - argument, and optionally change them by calling - the update method on this object, passing an array - of ranges in the same format. The object also contains - an origin property holding the origin string passed - to the selection-changing method, if any. Handlers for this - event have the same restriction - as "beforeChange" - handlers — they should not do anything to directly update the - state of the editor.
- -
"viewportChange" (instance: CodeMirror, from: number, to: number)
-
Fires whenever the view port of - the editor changes (due to scrolling, editing, or any other - factor). The from and to arguments - give the new start and end of the viewport.
- -
"swapDoc" (instance: CodeMirror, oldDoc: Doc)
-
This is signalled when the editor's document is replaced - using the swapDoc - method.
- -
"gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
-
Fires when the editor gutter (the line-number area) is - clicked. Will pass the editor instance as first argument, the - (zero-based) number of the line that was clicked as second - argument, the CSS class of the gutter that was clicked as third - argument, and the raw mousedown event object as - fourth argument.
- -
"gutterContextMenu" (instance: CodeMirror, line: integer, gutter: string, contextMenu: Event: Event)
-
Fires when the editor gutter (the line-number area) - receives a contextmenu event. Will pass the editor - instance as first argument, the (zero-based) number of the line - that was clicked as second argument, the CSS class of the - gutter that was clicked as third argument, and the raw - contextmenu mouse event object as fourth argument. - You can preventDefault the event, to signal that - CodeMirror should do no further handling.
- -
"focus" (instance: CodeMirror, event: Event)
-
Fires whenever the editor is focused.
- -
"blur" (instance: CodeMirror, event: Event)
-
Fires whenever the editor is unfocused.
- -
"scroll" (instance: CodeMirror)
-
Fires when the editor is scrolled.
- -
"refresh" (instance: CodeMirror)
-
Fires when the editor is refreshed - or resized. Mostly useful to invalidate - cached values that depend on the editor or character size.
- -
"optionChange" (instance: CodeMirror, option: string)
-
Dispatched every time an option is changed with setOption.
- -
"scrollCursorIntoView" (instance: CodeMirror, event: Event)
-
Fires when the editor tries to scroll its cursor into view. - Can be hooked into to take care of additional scrollable - containers around the editor. When the event object has - its preventDefault method called, CodeMirror will - not itself try to scroll the window.
- -
"update" (instance: CodeMirror)
-
Will be fired whenever CodeMirror updates its DOM display.
- -
"renderLine" (instance: CodeMirror, line: LineHandle, element: Element)
-
Fired whenever a line is (re-)rendered to the DOM. Fired - right after the DOM element is built, before it is - added to the document. The handler may mess with the style of - the resulting element, or add event handlers, but - should not try to change the state of the editor.
- -
"mousedown", - "dblclick", "touchstart", "contextmenu", - "keydown", "keypress", - "keyup", "cut", "copy", "paste", - "dragstart", "dragenter", - "dragover", "dragleave", - "drop" - (instance: CodeMirror, event: Event)
-
Fired when CodeMirror is handling a DOM event of this type. - You can preventDefault the event, or give it a - truthy codemirrorIgnore property, to signal that - CodeMirror should do no further handling.
-
- -

Document objects (instances - of CodeMirror.Doc) emit the - following events:

- -
-
"change" (doc: CodeMirror.Doc, changeObj: object)
-
Fired whenever a change occurs to the - document. changeObj has a similar type as the - object passed to the - editor's "change" - event.
- -
"beforeChange" (doc: CodeMirror.Doc, change: object)
-
See the description of the - same event on editor instances.
- -
"cursorActivity" (doc: CodeMirror.Doc)
-
Fired whenever the cursor or selection in this document - changes.
- -
"beforeSelectionChange" (doc: CodeMirror.Doc, selection: {head, anchor})
-
Equivalent to - the event by the same - name as fired on editor instances.
-
- -

Line handles (as returned by, for - example, getLineHandle) - support these events:

- -
-
"delete" ()
-
Will be fired when the line object is deleted. A line object - is associated with the start of the line. Mostly useful - when you need to find out when your gutter - markers on a given line are removed.
-
"change" (line: LineHandle, changeObj: object)
-
Fires when the line's text content is changed in any way - (but the line is not deleted outright). The change - object is similar to the one passed - to change event on the editor - object.
-
- -

Marked range handles (CodeMirror.TextMarker), as returned - by markText - and setBookmark, emit the - following events:

- -
-
"beforeCursorEnter" ()
-
Fired when the cursor enters the marked range. From this - event handler, the editor state may be inspected - but not modified, with the exception that the range on - which the event fires may be cleared.
-
"clear" (from: {line, ch}, to: {line, ch})
-
Fired when the range is cleared, either through cursor - movement in combination - with clearOnEnter - or through a call to its clear() method. Will only - be fired once per handle. Note that deleting the range through - text editing does not fire this event, because an undo action - might bring the range back into existence. from - and to give the part of the document that the range - spanned when it was cleared.
-
"hide" ()
-
Fired when the last part of the marker is removed from the - document by editing operations.
-
"unhide" ()
-
Fired when, after the marker was removed by editing, a undo - operation brought the marker back.
-
- -

Line widgets (CodeMirror.LineWidget), returned - by addLineWidget, fire - these events:

- -
-
"redraw" ()
-
Fired whenever the editor re-adds the widget to the DOM. - This will happen once right after the widget is added (if it is - scrolled into view), and then again whenever it is scrolled out - of view and back in again, or when changes to the editor options - or the line the widget is on require the widget to be - redrawn.
-
-
- -
-

Key Maps

- -

Key maps are ways to associate keys and mouse buttons with - functionality. A key map is an object mapping strings that - identify the buttons to functions that implement their - functionality.

- -

The CodeMirror distributions comes - with Emacs, Vim, - and Sublime Text-style keymaps.

- -

Keys are identified either by name or by character. - The CodeMirror.keyNames object defines names for - common keys and associates them with their key codes. Examples of - names defined here are Enter, F5, - and Q. These can be prefixed - with Shift-, Cmd-, Ctrl-, - and Alt- to specify a modifier. So for - example, Shift-Ctrl-Space would be a valid key - identifier.

- -

Common example: map the Tab key to insert spaces instead of a tab - character.

- -
-editor.setOption("extraKeys", {
-  Tab: function(cm) {
-    var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
-    cm.replaceSelection(spaces);
-  }
-});
- -

Alternatively, a character can be specified directly by - surrounding it in single quotes, for example '$' - or 'q'. Due to limitations in the way browsers fire - key events, these may not be prefixed with modifiers.

- -

To bind mouse buttons, use the names `LeftClick`, - `MiddleClick`, and `RightClick`. These can also be prefixed with - modifiers, and in addition, the word `Double` or `Triple` can be - put before `Click` (as in `LeftDoubleClick`) to bind a double- or - triple-click. The function for such a binding is passed the - position that was clicked as second argument.

- -

Multi-stroke key bindings can be specified - by separating the key names by spaces in the property name, for - example Ctrl-X Ctrl-V. When a map contains - multi-stoke bindings or keys with modifiers that are not specified - in the default order (Shift-Cmd-Ctrl-Alt), you must - call CodeMirror.normalizeKeyMap on it before it can - be used. This function takes a keymap and modifies it to normalize - modifier order and properly recognize multi-stroke bindings. It - will return the keymap itself.

- -

The CodeMirror.keyMap object associates key maps - with names. User code and key map definitions can assign extra - properties to this object. Anywhere where a key map is expected, a - string can be given, which will be looked up in this object. It - also contains the "default" key map holding the - default bindings.

- -

The values of properties in key maps can be either functions of - a single argument (the CodeMirror instance), strings, or - false. Strings refer - to commands, which are described below. If - the property is set to false, CodeMirror leaves - handling of the key up to the browser. A key handler function may - return CodeMirror.Pass to indicate that it has - decided not to handle the key, and other handlers (or the default - behavior) should be given a turn.

- -

Keys mapped to command names that start with the - characters "go" or to functions that have a - truthy motion property (which should be used for - cursor-movement actions) will be fired even when an - extra Shift modifier is present (i.e. "Up": - "goLineUp" matches both up and shift-up). This is used to - easily implement shift-selection.

- -

Key maps can defer to each other by defining - a fallthrough property. This indicates that when a - key is not found in the map itself, one or more other maps should - be searched. It can hold either a single key map or an array of - key maps.

- -

When a key map needs to set something up when it becomes - active, or tear something down when deactivated, it can - contain attach and/or detach properties, - which should hold functions that take the editor instance and the - next or previous keymap. Note that this only works for the - top-level keymap, not for fallthrough - maps or maps added - with extraKeys - or addKeyMap.

-
- -
-

Commands

- -

Commands are parameter-less actions that can be performed on an - editor. Their main use is for key bindings. Commands are defined by - adding properties to the CodeMirror.commands object. - A number of common commands are defined by the library itself, - most of them used by the default key bindings. The value of a - command property must be a function of one argument (an editor - instance).

- -

Some of the commands below are referenced in the default - key map, but not defined by the core library. These are intended to - be defined by user code or addons.

- -

Commands can also be run with - the execCommand - method.

- -
-
selectAllCtrl-A (PC), Cmd-A (Mac)
-
Select the whole content of the editor.
- -
singleSelectionEsc
-
When multiple selections are present, this deselects all but - the primary selection.
- -
killLineCtrl-K (Mac)
-
Emacs-style line killing. Deletes the part of the line after - the cursor. If that consists only of whitespace, the newline at - the end of the line is also deleted.
- -
deleteLineCtrl-D (PC), Cmd-D (Mac)
-
Deletes the whole line under the cursor, including newline at the end.
- -
delLineLeft
-
Delete the part of the line before the cursor.
- -
delWrappedLineLeftCmd-Backspace (Mac)
-
Delete the part of the line from the left side of the visual line the cursor is on to the cursor.
- -
delWrappedLineRightCmd-Delete (Mac)
-
Delete the part of the line from the cursor to the right side of the visual line the cursor is on.
- -
undoCtrl-Z (PC), Cmd-Z (Mac)
-
Undo the last change. Note that, because browsers still - don't make it possible for scripts to react to or customize the - context menu, selecting undo (or redo) from the context menu in - a CodeMirror instance does not work.
- -
redoCtrl-Y (PC), Shift-Cmd-Z (Mac), Cmd-Y (Mac)
-
Redo the last undone change.
- -
undoSelectionCtrl-U (PC), Cmd-U (Mac)
-
Undo the last change to the selection, or if there are no - selection-only changes at the top of the history, undo the last - change.
- -
redoSelectionAlt-U (PC), Shift-Cmd-U (Mac)
-
Redo the last change to the selection, or the last text change if - no selection changes remain.
- -
goDocStartCtrl-Home (PC), Cmd-Up (Mac), Cmd-Home (Mac)
-
Move the cursor to the start of the document.
- -
goDocEndCtrl-End (PC), Cmd-End (Mac), Cmd-Down (Mac)
-
Move the cursor to the end of the document.
- -
goLineStartAlt-Left (PC), Ctrl-A (Mac)
-
Move the cursor to the start of the line.
- -
goLineStartSmartHome
-
Move to the start of the text on the line, or if we are - already there, to the actual start of the line (including - whitespace).
- -
goLineEndAlt-Right (PC), Ctrl-E (Mac)
-
Move the cursor to the end of the line.
- -
goLineRightCmd-Right (Mac)
-
Move the cursor to the right side of the visual line it is on.
- -
goLineLeftCmd-Left (Mac)
-
Move the cursor to the left side of the visual line it is on. If - this line is wrapped, that may not be the start of the line.
- -
goLineLeftSmart
-
Move the cursor to the left side of the visual line it is - on. If that takes it to the start of the line, behave - like goLineStartSmart.
- -
goLineUpUp, Ctrl-P (Mac)
-
Move the cursor up one line.
- -
goLineDownDown, Ctrl-N (Mac)
-
Move down one line.
- -
goPageUpPageUp, Shift-Ctrl-V (Mac)
-
Move the cursor up one screen, and scroll up by the same distance.
- -
goPageDownPageDown, Ctrl-V (Mac)
-
Move the cursor down one screen, and scroll down by the same distance.
- -
goCharLeftLeft, Ctrl-B (Mac)
-
Move the cursor one character left, going to the previous line - when hitting the start of line.
- -
goCharRightRight, Ctrl-F (Mac)
-
Move the cursor one character right, going to the next line - when hitting the end of line.
- -
goColumnLeft
-
Move the cursor one character left, but don't cross line boundaries.
- -
goColumnRight
-
Move the cursor one character right, don't cross line boundaries.
- -
goWordLeftAlt-B (Mac)
-
Move the cursor to the start of the previous word.
- -
goWordRightAlt-F (Mac)
-
Move the cursor to the end of the next word.
- -
goGroupLeftCtrl-Left (PC), Alt-Left (Mac)
-
Move to the left of the group before the cursor. A group is - a stretch of word characters, a stretch of punctuation - characters, a newline, or a stretch of more than one - whitespace character.
- -
goGroupRightCtrl-Right (PC), Alt-Right (Mac)
-
Move to the right of the group after the cursor - (see above).
- -
delCharBeforeShift-Backspace, Ctrl-H (Mac)
-
Delete the character before the cursor.
- -
delCharAfterDelete, Ctrl-D (Mac)
-
Delete the character after the cursor.
- -
delWordBeforeAlt-Backspace (Mac)
-
Delete up to the start of the word before the cursor.
- -
delWordAfterAlt-D (Mac)
-
Delete up to the end of the word after the cursor.
- -
delGroupBeforeCtrl-Backspace (PC), Alt-Backspace (Mac)
-
Delete to the left of the group before the cursor.
- -
delGroupAfterCtrl-Delete (PC), Ctrl-Alt-Backspace (Mac), Alt-Delete (Mac)
-
Delete to the start of the group after the cursor.
- -
indentAutoShift-Tab
-
Auto-indent the current line or selection.
- -
indentMoreCtrl-] (PC), Cmd-] (Mac)
-
Indent the current line or selection by one indent unit.
- -
indentLessCtrl-[ (PC), Cmd-[ (Mac)
-
Dedent the current line or selection by one indent unit.
- -
insertTab
-
Insert a tab character at the cursor.
- -
insertSoftTab
-
Insert the amount of spaces that match the width a tab at - the cursor position would have.
- -
defaultTabTab
-
If something is selected, indent it by - one indent unit. If nothing is - selected, insert a tab character.
- -
transposeCharsCtrl-T (Mac)
-
Swap the characters before and after the cursor.
- -
newlineAndIndentEnter
-
Insert a newline and auto-indent the new line.
- -
toggleOverwriteInsert
-
Flip the overwrite flag.
- -
saveCtrl-S (PC), Cmd-S (Mac)
-
Not defined by the core library, only referred to in - key maps. Intended to provide an easy way for user code to define - a save command.
- -
findCtrl-F (PC), Cmd-F (Mac)
-
findNextCtrl-G (PC), Cmd-G (Mac)
-
findPrevShift-Ctrl-G (PC), Shift-Cmd-G (Mac)
-
replaceShift-Ctrl-F (PC), Cmd-Alt-F (Mac)
-
replaceAllShift-Ctrl-R (PC), Shift-Cmd-Alt-F (Mac)
-
Not defined by the core library, but defined in - the search addon (or custom client - addons).
- -
- -
- -
-

Customized Styling

- -

Up to a certain extent, CodeMirror's look can be changed by - modifying style sheet files. The style sheets supplied by modes - simply provide the colors for that mode, and can be adapted in a - very straightforward way. To style the editor itself, it is - possible to alter or override the styles defined - in codemirror.css.

- -

Some care must be taken there, since a lot of the rules in this - file are necessary to have CodeMirror function properly. Adjusting - colors should be safe, of course, and with some care a lot of - other things can be changed as well. The CSS classes defined in - this file serve the following roles:

- -
-
CodeMirror
-
The outer element of the editor. This should be used for the - editor width, height, borders and positioning. Can also be used - to set styles that should hold for everything inside the editor - (such as font and font size), or to set a background. Setting - this class' height style to auto will - make the editor resize to fit its - content (it is recommended to also set - the viewportMargin - option to Infinity when doing this.
- -
CodeMirror-focused
-
Whenever the editor is focused, the top element gets this - class. This is used to hide the cursor and give the selection a - different color when the editor is not focused.
- -
CodeMirror-gutters
-
This is the backdrop for all gutters. Use it to set the - default gutter background color, and optionally add a border on - the right of the gutters.
- -
CodeMirror-linenumbers
-
Use this for giving a background or width to the line number - gutter.
- -
CodeMirror-linenumber
-
Used to style the actual individual line numbers. These - won't be children of the CodeMirror-linenumbers - (plural) element, but rather will be absolutely positioned to - overlay it. Use this to set alignment and text properties for - the line numbers.
- -
CodeMirror-lines
-
The visible lines. This is where you specify vertical - padding for the editor content.
- -
CodeMirror-cursor
-
The cursor is a block element that is absolutely positioned. - You can make it look whichever way you want.
- -
CodeMirror-selected
-
The selection is represented by span elements - with this class.
- -
CodeMirror-matchingbracket, - CodeMirror-nonmatchingbracket
-
These are used to style matched (or unmatched) brackets.
-
- -

If your page's style sheets do funky things to - all div or pre elements (you probably - shouldn't do that), you'll have to define rules to cancel these - effects out again for elements under the CodeMirror - class.

- -

Themes are also simply CSS files, which define colors for - various syntactic elements. See the files in - the theme directory.

-
- -
-

Programming API

- -

A lot of CodeMirror features are only available through its - API. Thus, you need to write code (or - use addons) if you want to expose them to - your users.

- -

Whenever points in the document are represented, the API uses - objects with line and ch properties. - Both are zero-based. CodeMirror makes sure to 'clip' any positions - passed by client code so that they fit inside the document, so you - shouldn't worry too much about sanitizing your coordinates. If you - give ch a value of null, or don't - specify it, it will be replaced with the length of the specified - line. Such positions may also have a sticky property - holding "before" or "after", whether the - position is associated with the character before or after it. This - influences, for example, where the cursor is drawn on a - line-break or bidi-direction boundary.

- -

Methods prefixed with doc. can, unless otherwise - specified, be called both on CodeMirror (editor) - instances and CodeMirror.Doc instances. Methods - prefixed with cm. are only available - on CodeMirror instances.

- -

Constructor

- -

Constructing an editor instance is done with - the CodeMirror(place: Element|fn(Element), - ?option: object) constructor. If the place - argument is a DOM element, the editor will be appended to it. If - it is a function, it will be called, and is expected to place the - editor into the document. options may be an element - mapping option names to values. The options - that it doesn't explicitly specify (or all options, if it is not - passed) will be taken - from CodeMirror.defaults.

- -

Note that the options object passed to the constructor will be - mutated when the instance's options - are changed, so you shouldn't share such - objects between instances.

- -

See CodeMirror.fromTextArea - for another way to construct an editor instance.

- -

Content manipulation methods

- -
-
doc.getValue(?separator: string) → string
-
Get the current editor content. You can pass it an optional - argument to specify the string to be used to separate lines - (defaults to "\n").
-
doc.setValue(content: string)
-
Set the editor content.
- -
doc.getRange(from: {line, ch}, to: {line, ch}, ?separator: string) → string
-
Get the text between the given points in the editor, which - should be {line, ch} objects. An optional third - argument can be given to indicate the line separator string to - use (defaults to "\n").
-
doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch}, ?origin: string)
-
Replace the part of the document between from - and to with the given string. from - and to must be {line, ch} - objects. to can be left off to simply insert the - string at position from. When origin - is given, it will be passed on - to "change" events, and - its first letter will be used to determine whether this change - can be merged with previous history events, in the way described - for selection origins.
- -
doc.getLine(n: integer) → string
-
Get the content of line n.
- -
doc.lineCount() → integer
-
Get the number of lines in the editor.
-
doc.firstLine() → integer
-
Get the number of first line in the editor. This will - usually be zero but for linked sub-views, - or documents instantiated with a non-zero - first line, it might return other values.
-
doc.lastLine() → integer
-
Get the number of last line in the editor. This will - usually be doc.lineCount() - 1, - but for linked sub-views, - it might return other values.
- -
doc.getLineHandle(num: integer) → LineHandle
-
Fetches the line handle for the given line number.
-
doc.getLineNumber(handle: LineHandle) → integer
-
Given a line handle, returns the current position of that - line (or null when it is no longer in the - document).
-
doc.eachLine(f: (line: LineHandle))
-
doc.eachLine(start: integer, end: integer, f: (line: LineHandle))
-
Iterate over the whole document, or if start - and end line numbers are given, the range - from start up to (not including) end, - and call f for each line, passing the line handle. - This is a faster way to visit a range of line handlers than - calling getLineHandle - for each of them. Note that line handles have - a text property containing the line's content (as a - string).
- -
doc.markClean()
-
Set the editor content as 'clean', a flag that it will - retain until it is edited, and which will be set again when such - an edit is undone again. Useful to track whether the content - needs to be saved. This function is deprecated in favor - of changeGeneration, - which allows multiple subsystems to track different notions of - cleanness without interfering.
-
doc.changeGeneration(?closeEvent: boolean) → integer
-
Returns a number that can later be passed - to isClean to test whether - any edits were made (and not undone) in the meantime. - If closeEvent is true, the current history event - will be ‘closed’, meaning it can't be combined with further - changes (rapid typing or deleting events are typically - combined).
-
doc.isClean(?generation: integer) → boolean
-
Returns whether the document is currently clean — not - modified since initialization or the last call - to markClean if no - argument is passed, or since the matching call - to changeGeneration - if a generation value is given.
-
- -

Cursor and selection methods

- -
-
doc.getSelection(?lineSep: string) → string
-
Get the currently selected code. Optionally pass a line - separator to put between the lines in the output. When multiple - selections are present, they are concatenated with instances - of lineSep in between.
-
doc.getSelections(?lineSep: string) → array<string>
-
Returns an array containing a string for each selection, - representing the content of the selections.
- -
doc.replaceSelection(replacement: string, ?select: string)
-
Replace the selection(s) with the given string. By default, - the new selection ends up after the inserted text. The - optional select argument can be used to change - this—passing "around" will cause the new text to be - selected, passing "start" will collapse the - selection to the start of the inserted text.
-
doc.replaceSelections(replacements: array<string>, ?select: string)
-
The length of the given array should be the same as the - number of active selections. Replaces the content of the - selections with the strings in the array. - The select argument works the same as - in replaceSelection.
- -
doc.getCursor(?start: string) → {line, ch}
-
Retrieve one end of the primary - selection. start is an optional string indicating - which end of the selection to return. It may - be "from", "to", "head" - (the side of the selection that moves when you press - shift+arrow), or "anchor" (the fixed side of the - selection). Omitting the argument is the same as - passing "head". A {line, ch} object - will be returned.
-
doc.listSelections() → array<{anchor, head}>
-
Retrieves a list of all current selections. These will - always be sorted, and never overlap (overlapping selections are - merged). Each object in the array contains anchor - and head properties referring to {line, - ch} objects.
- -
doc.somethingSelected() → boolean
-
Return true if any text is selected.
-
doc.setCursor(pos: {line, ch}|number, ?ch: number, ?options: object)
-
Set the cursor position. You can either pass a - single {line, ch} object, or the line and the - character as two separate parameters. Will replace all - selections with a single, empty selection at the given position. - The supported options are the same as for setSelection.
- -
doc.setSelection(anchor: {line, ch}, ?head: {line, ch}, ?options: object)
-
Set a single selection range. anchor - and head should be {line, ch} - objects. head defaults to anchor when - not given. These options are supported: -
-
scroll: boolean
-
Determines whether the selection head should be scrolled - into view. Defaults to true.
-
origin: string
-
Determines whether the selection history event may be - merged with the previous one. When an origin starts with the - character +, and the last recorded selection had - the same origin and was similar (close - in time, both - collapsed or both non-collapsed), the new one will replace the - old one. When it starts with *, it will always - replace the previous event (if that had the same origin). - Built-in motion uses the "+move" origin. User input uses the "+input" origin.
-
bias: number
-
Determine the direction into which the selection endpoints - should be adjusted when they fall inside - an atomic range. Can be either -1 - (backward) or 1 (forward). When not given, the bias will be - based on the relative position of the old selection—the editor - will try to move further away from that, to prevent getting - stuck.
-
- -
doc.setSelections(ranges: array<{anchor, head}>, ?primary: integer, ?options: object)
-
Sets a new set of selections. There must be at least one - selection in the given array. When primary is a - number, it determines which selection is the primary one. When - it is not given, the primary index is taken from the previous - selection, or set to the last range if the previous selection - had less ranges than the new one. Supports the same options - as setSelection.
-
doc.addSelection(anchor: {line, ch}, ?head: {line, ch})
-
Adds a new selection to the existing set of selections, and - makes it the primary selection.
- -
doc.extendSelection(from: {line, ch}, ?to: {line, ch}, ?options: object)
-
Similar - to setSelection, but - will, if shift is held or - the extending flag is set, move the - head of the selection while leaving the anchor at its current - place. to is optional, and can be passed to ensure - a region (for example a word or paragraph) will end up selected - (in addition to whatever lies between that region and the - current anchor). When multiple selections are present, all but - the primary selection will be dropped by this method. - Supports the same options as setSelection.
-
doc.extendSelections(heads: array<{line, ch}>, ?options: object)
-
An equivalent - of extendSelection - that acts on all selections at once.
-
doc.extendSelectionsBy(f: function(range: {anchor, head}) → {line, ch}), ?options: object)
-
Applies the given function to all existing selections, and - calls extendSelections - on the result.
-
doc.setExtending(value: boolean)
-
Sets or clears the 'extending' flag, which acts similar to - the shift key, in that it will cause cursor movement and calls - to extendSelection - to leave the selection anchor in place.
-
doc.getExtending() → boolean
-
Get the value of the 'extending' flag.
- -
cm.hasFocus() → boolean
-
Tells you whether the editor currently has focus.
- -
cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
-
Used to find the target position for horizontal cursor - motion. start is a {line, ch} - object, amount an integer (may be negative), - and unit one of the - string "char", "column", - or "word". Will return a position that is produced - by moving amount times the distance specified - by unit. When visually is true, motion - in right-to-left text will be visual rather than logical. When - the motion was clipped by hitting the end or start of the - document, the returned value will have a hitSide - property set to true.
-
cm.findPosV(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}
-
Similar to findPosH, - but used for vertical motion. unit may - be "line" or "page". The other - arguments and the returned value have the same interpretation as - they have in findPosH.
- -
cm.findWordAt(pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}
-
Returns the start and end of the 'word' (the stretch of - letters, whitespace, or punctuation) at the given position.
-
- -

Configuration methods

- -
-
cm.setOption(option: string, value: any)
-
Change the configuration of the editor. option - should the name of an option, - and value should be a valid value for that - option.
-
cm.getOption(option: string) → any
-
Retrieves the current value of the given option for this - editor instance.
- -
cm.addKeyMap(map: object, bottom: boolean)
-
Attach an additional key map to the - editor. This is mostly useful for addons that need to register - some key handlers without trampling on - the extraKeys - option. Maps added in this way have a higher precedence than - the extraKeys - and keyMap options, - and between them, the maps added earlier have a lower precedence - than those added later, unless the bottom argument - was passed, in which case they end up below other key maps added - with this method.
-
cm.removeKeyMap(map: object)
-
Disable a keymap added - with addKeyMap. Either - pass in the key map object itself, or a string, which will be - compared against the name property of the active - key maps.
- -
cm.addOverlay(mode: string|object, ?options: object)
-
Enable a highlighting overlay. This is a stateless mini-mode - that can be used to add extra highlighting. For example, - the search addon uses it to - highlight the term that's currently being - searched. mode can be a mode - spec or a mode object (an object with - a token method). - The options parameter is optional. If given, it - should be an object, optionally containing the following options: -
-
opaque: bool
-
Defaults to off, but can be given to allow the overlay - styling, when not null, to override the styling of - the base mode entirely, instead of the two being applied - together.
-
priority: number
-
Determines the ordering in which the overlays are - applied. Those with high priority are applied after those - with lower priority, and able to override the opaqueness of - the ones that come before. Defaults to 0.
-
-
- -
cm.removeOverlay(mode: string|object)
-
Pass this the exact value passed for the mode - parameter to addOverlay, - or a string that corresponds to the name property of - that value, to remove an overlay again.
- -
cm.on(type: string, func: (...args))
-
Register an event handler for the given event type (a - string) on the editor instance. There is also - a CodeMirror.on(object, type, func) version - that allows registering of events on any object.
-
cm.off(type: string, func: (...args))
-
Remove an event handler on the editor instance. An - equivalent CodeMirror.off(object, type, - func) also exists.
-
- -

Document management methods

- -

Each editor is associated with an instance - of CodeMirror.Doc, its document. A document - represents the editor content, plus a selection, an undo history, - and a mode. A document can only be - associated with a single editor at a time. You can create new - documents by calling the CodeMirror.Doc(text: string, mode: - Object, firstLineNumber: ?number, lineSeparator: ?string) - constructor. The last three arguments are optional and can be used - to set a mode for the document, make it start at a line number - other than 0, and set a specific line separator respectively.

- -
-
cm.getDoc() → Doc
-
Retrieve the currently active document from an editor.
-
doc.getEditor() → CodeMirror
-
Retrieve the editor associated with a document. May - return null.
- -
cm.swapDoc(doc: CodeMirror.Doc) → Doc
-
Attach a new document to the editor. Returns the old - document, which is now no longer associated with an editor.
- -
doc.copy(copyHistory: boolean) → Doc
-
Create an identical copy of the given doc. - When copyHistory is true, the history will also be - copied. Can not be called directly on an editor.
- -
doc.linkedDoc(options: object) → Doc
-
Create a new document that's linked to the target document. - Linked documents will stay in sync (changes to one are also - applied to the other) until unlinked. - These are the options that are supported: -
-
sharedHist: boolean
-
When turned on, the linked copy will share an undo - history with the original. Thus, something done in one of - the two can be undone in the other, and vice versa.
-
from: integer
-
to: integer
-
Can be given to make the new document a subview of the - original. Subviews only show a given range of lines. Note - that line coordinates inside the subview will be consistent - with those of the parent, so that for example a subview - starting at line 10 will refer to its first line as line 10, - not 0.
-
mode: string|object
-
By default, the new document inherits the mode of the - parent. This option can be set to - a mode spec to give it a - different mode.
-
-
doc.unlinkDoc(doc: CodeMirror.Doc)
-
Break the link between two documents. After calling this, - changes will no longer propagate between the documents, and, if - they had a shared history, the history will become - separate.
-
doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean))
-
Will call the given function for all documents linked to the - target document. It will be passed two arguments, the linked document - and a boolean indicating whether that document shares history - with the target.
-
- -

History-related methods

- -
-
doc.undo()
-
Undo one edit (if any undo events are stored).
-
doc.redo()
-
Redo one undone edit.
- -
doc.undoSelection()
-
Undo one edit or selection change.
-
doc.redoSelection()
-
Redo one undone edit or selection change.
- -
doc.historySize() → {undo: integer, redo: integer}
-
Returns an object with {undo, redo} properties, - both of which hold integers, indicating the amount of stored - undo and redo operations.
-
doc.clearHistory()
-
Clears the editor's undo history.
-
doc.getHistory() → object
-
Get a (JSON-serializable) representation of the undo history.
-
doc.setHistory(history: object)
-
Replace the editor's undo history with the one provided, - which must be a value as returned - by getHistory. Note that - this will have entirely undefined results if the editor content - isn't also the same as it was when getHistory was - called.
-
- -

Text-marking methods

- -
-
doc.markText(from: {line, ch}, to: {line, ch}, ?options: object) → TextMarker
-
Can be used to mark a range of text with a specific CSS - class name. from and to should - be {line, ch} objects. The options - parameter is optional. When given, it should be an object that - may contain the following configuration options: -
-
className: string
-
Assigns a CSS class to the marked stretch of text.
-
inclusiveLeft: boolean
-
Determines whether - text inserted on the left of the marker will end up inside - or outside of it.
-
inclusiveRight: boolean
-
Like inclusiveLeft, - but for the right side.
-
selectLeft: boolean
-
For atomic ranges, determines whether the cursor is allowed - to be placed directly to the left of the range. Has no effect on - non-atomic ranges.
-
selectRight: boolean
-
Like selectLeft, - but for the right side.
-
atomic: boolean
-
Atomic ranges act as a single unit when cursor movement is - concerned—i.e. it is impossible to place the cursor inside of - them. You can control whether the cursor is allowed to be placed - directly before or after them using selectLeft - or selectRight. If selectLeft - (or right) is not provided, then inclusiveLeft (or - right) will control this behavior.
-
collapsed: boolean
-
Collapsed ranges do not show up in the display. Setting a - range to be collapsed will automatically make it atomic.
-
clearOnEnter: boolean
-
When enabled, will cause the mark to clear itself whenever - the cursor enters its range. This is mostly useful for - text-replacement widgets that need to 'snap open' when the - user tries to edit them. The - "clear" event - fired on the range handle can be used to be notified when this - happens.
-
clearWhenEmpty: boolean
-
Determines whether the mark is automatically cleared when - it becomes empty. Default is true.
-
replacedWith: Element
-
Use a given node to display this range. Implies both - collapsed and atomic. The given DOM node must be an - inline element (as opposed to a block element).
-
handleMouseEvents: boolean
-
When replacedWith is given, this determines - whether the editor will capture mouse and drag events - occurring in this widget. Default is false—the events will be - left alone for the default browser handler, or specific - handlers on the widget, to capture.
-
readOnly: boolean
-
A read-only span can, as long as it is not cleared, not be - modified except by - calling setValue to reset - the whole document. Note: adding a read-only span - currently clears the undo history of the editor, because - existing undo events being partially nullified by read-only - spans would corrupt the history (in the current - implementation).
-
addToHistory: boolean
-
When set to true (default is false), adding this marker - will create an event in the undo history that can be - individually undone (clearing the marker).
-
startStyle: string
Can be used to specify - an extra CSS class to be applied to the leftmost span that - is part of the marker.
-
endStyle: string
Equivalent - to startStyle, but for the rightmost span.
-
css: string
-
A string of CSS to be applied to the covered text. For example "color: #fe3".
-
attributes: object
-
When given, add the attributes in the given object to the - elements created for the marked text. Adding class or - style attributes this way is not supported.
-
shared: boolean
When the - target document is linked to other - documents, you can set shared to true to make the - marker appear in all documents. By default, a marker appears - only in its target document.
-
- The method will return an object that represents the marker - (with constructor CodeMirror.TextMarker), which - exposes three methods: - clear(), to remove the mark, - find(), which returns - a {from, to} object (both holding document - positions), indicating the current position of the marked range, - or undefined if the marker is no longer in the - document, and finally changed(), - which you can call if you've done something that might change - the size of the marker (for example changing the content of - a replacedWith - node), and want to cheaply update the display.
- -
doc.setBookmark(pos: {line, ch}, ?options: object) → TextMarker
-
Inserts a bookmark, a handle that follows the text around it - as it is being edited, at the given position. A bookmark has two - methods find() and clear(). The first - returns the current position of the bookmark, if it is still in - the document, and the second explicitly removes the bookmark. - The options argument is optional. If given, the following - properties are recognized: -
-
widget: Element
Can be used to display a DOM - node at the current location of the bookmark (analogous to - the replacedWith - option to markText).
-
insertLeft: boolean
By default, text typed - when the cursor is on top of the bookmark will end up to the - right of the bookmark. Set this option to true to make it go - to the left instead.
-
shared: boolean
See - the corresponding option - to markText.
-
handleMouseEvents: boolean
-
As with markText, - this determines whether mouse events on the widget inserted - for this bookmark are handled by CodeMirror. The default is - false.
-
- -
doc.findMarks(from: {line, ch}, to: {line, ch}) → array<TextMarker>
-
Returns an array of all the bookmarks and marked ranges - found between the given positions (non-inclusive).
-
doc.findMarksAt(pos: {line, ch}) → array<TextMarker>
-
Returns an array of all the bookmarks and marked ranges - present at the given position.
-
doc.getAllMarks() → array<TextMarker>
-
Returns an array containing all marked ranges in the document.
-
- -

Widget, gutter, and decoration methods

- -
-
doc.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle
-
Sets the gutter marker for the given gutter (identified by - its CSS class, see - the gutters option) - to the given value. Value can be either null, to - clear the marker, or a DOM element, to set it. The DOM element - will be shown in the specified gutter next to the specified - line.
- -
doc.clearGutter(gutterID: string)
-
Remove all gutter markers in - the gutter with the given ID.
- -
doc.addLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
-
Set a CSS class name for the given line. line - can be a number or a line handle. where determines - to which element this class should be applied, can can be one - of "text" (the text element, which lies in front of - the selection), "background" (a background element - that will be behind the selection), "gutter" (the - line's gutter space), or "wrap" (the wrapper node - that wraps all of the line's elements, including gutter - elements). class should be the name of the class to - apply.
- -
doc.removeLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
-
Remove a CSS class from a line. line can be a - line handle or number. where should be one - of "text", "background", - or "wrap" - (see addLineClass). class - can be left off to remove all classes for the specified node, or - be a string to remove only a specific class.
- -
doc.lineInfo(line: integer|LineHandle) → object
-
Returns the line number, text content, and marker status of - the given line, which can be either a number or a line handle. - The returned object has the structure {line, handle, text, - gutterMarkers, textClass, bgClass, wrapClass, widgets}, - where gutterMarkers is an object mapping gutter IDs - to marker elements, and widgets is an array - of line widgets attached to this - line, and the various class properties refer to classes added - with addLineClass.
- -
cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean)
-
Puts node, which should be an absolutely - positioned DOM node, into the editor, positioned right below the - given {line, ch} position. - When scrollIntoView is true, the editor will ensure - that the entire node is visible (if possible). To remove the - widget again, simply use DOM methods (move it somewhere else, or - call removeChild on its parent).
- -
doc.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → LineWidget
-
Adds a line widget, an element shown below a line, spanning - the whole of the editor's width, and moving the lines below it - downwards. line should be either an integer or a - line handle, and node should be a DOM node, which - will be displayed below the given line. options, - when given, should be an object that configures the behavior of - the widget. The following options are supported (all default to - false): -
-
coverGutter: boolean
-
Whether the widget should cover the gutter.
-
noHScroll: boolean
-
Whether the widget should stay fixed in the face of - horizontal scrolling.
-
above: boolean
-
Causes the widget to be placed above instead of below - the text of the line.
-
handleMouseEvents: boolean
-
Determines whether the editor will capture mouse and - drag events occurring in this widget. Default is false—the - events will be left alone for the default browser handler, - or specific handlers on the widget, to capture.
-
insertAt: integer
-
By default, the widget is added below other widgets for - the line. This option can be used to place it at a different - position (zero for the top, N to put it after the Nth other - widget). Note that this only has effect once, when the - widget is created. -
className: string
-
Add an extra CSS class name to the wrapper element - created for the widget.
-
- Note that the widget node will become a descendant of nodes with - CodeMirror-specific CSS classes, and those classes might in some - cases affect it. This method returns an object that represents - the widget placement. It'll have a line property - pointing at the line handle that it is associated with, and the following methods: -
-
clear()
Removes the widget.
-
changed()
Call - this if you made some change to the widget's DOM node that - might affect its height. It'll force CodeMirror to update - the height of the line that contains the widget.
-
-
-
- -

Sizing, scrolling and positioning methods

- -
-
cm.setSize(width: number|string, height: number|string)
-
Programmatically set the size of the editor (overriding the - applicable CSS - rules). width and height - can be either numbers (interpreted as pixels) or CSS units - ("100%", for example). You can - pass null for either of them to indicate that that - dimension should not be changed.
- -
cm.scrollTo(x: number, y: number)
-
Scroll the editor to a given (pixel) position. Both - arguments may be left as null - or undefined to have no effect.
-
cm.getScrollInfo() → {left, top, width, height, clientWidth, clientHeight}
-
Get an {left, top, width, height, clientWidth, - clientHeight} object that represents the current scroll - position, the size of the scrollable area, and the size of the - visible area (minus scrollbars).
-
cm.scrollIntoView(what: {line, ch}|{left, top, right, bottom}|{from, to}|null, ?margin: number)
-
Scrolls the given position into view. what may - be null to scroll the cursor into view, - a {line, ch} position to scroll a character into - view, a {left, top, right, bottom} pixel range (in - editor-local coordinates), or a range {from, to} - containing either two character positions or two pixel squares. - The margin parameter is optional. When given, it - indicates the amount of vertical pixels around the given area - that should be made visible as well.
- -
cm.cursorCoords(where: boolean|{line, ch}, mode: string) → {left, top, bottom}
-
Returns an {left, top, bottom} object - containing the coordinates of the cursor position. - If mode is "local", they will be - relative to the top-left corner of the editable document. If it - is "page" or not given, they are relative to the - top-left corner of the page. If mode - is "window", the coordinates are relative to the - top-left corner of the currently visible (scrolled) - window. where can be a boolean indicating whether - you want the start (true) or the end - (false) of the selection, or, if a {line, - ch} object is given, it specifies the precise position at - which you want to measure.
-
cm.charCoords(pos: {line, ch}, ?mode: string) → {left, right, top, bottom}
-
Returns the position and dimensions of an arbitrary - character. pos should be a {line, ch} - object. This differs from cursorCoords in that - it'll give the size of the whole character, rather than just the - position that the cursor would have when it would sit at that - position.
-
cm.coordsChar(object: {left, top}, ?mode: string) → {line, ch}
-
Given an {left, top} object (e.g. coordinates of a mouse event) returns - the {line, ch} position that corresponds to it. The - optional mode parameter determines relative to what - the coordinates are interpreted. It may - be "window", "page" (the default), - or "local".
-
cm.lineAtHeight(height: number, ?mode: string) → number
-
Computes the line at the given pixel - height. mode can be one of the same strings - that coordsChar - accepts.
-
cm.heightAtLine(line: integer|LineHandle, ?mode: string, ?includeWidgets: bool) → number
-
Computes the height of the top of a line, in the coordinate - system specified by mode - (see coordsChar), which - defaults to "page". When a line below the bottom of - the document is specified, the returned value is the bottom of - the last line in the document. By default, the position of the - actual text is returned. If `includeWidgets` is true and the - line has line widgets, the position above the first line widget - is returned.
-
cm.defaultTextHeight() → number
-
Returns the line height of the default font for the editor.
-
cm.defaultCharWidth() → number
-
Returns the pixel width of an 'x' in the default font for - the editor. (Note that for non-monospace fonts, this is mostly - useless, and even for monospace fonts, non-ascii characters - might have a different width).
- -
cm.getViewport() → {from: number, to: number}
-
Returns a {from, to} object indicating the - start (inclusive) and end (exclusive) of the currently rendered - part of the document. In big documents, when most content is - scrolled out of view, CodeMirror will only render the visible - part, and a margin around it. See also - the viewportChange - event.
- -
cm.refresh()
-
If your code does something to change the size of the editor - element (window resizes are already listened for), or unhides - it, you should probably follow up by calling this method to - ensure CodeMirror is still looking as intended. See also - the autorefresh addon.
-
- -

Mode, state, and token-related methods

- -

When writing language-aware functionality, it can often be - useful to hook into the knowledge that the CodeMirror language - mode has. See the section on modes for a - more detailed description of how these work.

- -
-
doc.getMode() → object
-
Gets the (outer) mode object for the editor. Note that this - is distinct from getOption("mode"), which gives you - the mode specification, rather than the resolved, instantiated - mode object.
- -
cm.getModeAt(pos: {line, ch}) → object
-
Gets the inner mode at a given position. This will return - the same as getMode for - simple modes, but will return an inner mode for nesting modes - (such as htmlmixed).
- -
cm.getTokenAt(pos: {line, ch}, ?precise: boolean) → object
-
Retrieves information about the token the current mode found - before the given position (a {line, ch} object). The - returned object has the following properties: -
-
start
The character (on the given line) at which the token starts.
-
end
The character at which the token ends.
-
string
The token's string.
-
type
The token type the mode assigned - to the token, such as "keyword" - or "comment" (may also be null).
-
state
The mode's state at the end of this token.
-
- If precise is true, the token will be guaranteed to be accurate based on recent edits. If false or - not specified, the token will use cached state information, which will be faster but might not be accurate if - edits were recently made and highlighting has not yet completed. -
- -
cm.getLineTokens(line: integer, ?precise: boolean) → array<{start, end, string, type, state}>
-
This is similar - to getTokenAt, but - collects all tokens for a given line into an array. It is much - cheaper than repeatedly calling getTokenAt, which - re-parses the part of the line before the token for every call.
- -
cm.getTokenTypeAt(pos: {line, ch}) → string
-
This is a (much) cheaper version - of getTokenAt useful for - when you just need the type of the token at a given position, - and no other information. Will return null for - unstyled tokens, and a string, potentially containing multiple - space-separated style names, otherwise.
- -
cm.getHelpers(pos: {line, ch}, type: string) → array<helper>
-
Fetch the set of applicable helper values for the given - position. Helpers provide a way to look up functionality - appropriate for a mode. The type argument provides - the helper namespace (see - registerHelper), in - which the values will be looked up. When the mode itself has a - property that corresponds to the type, that - directly determines the keys that are used to look up the helper - values (it may be either a single string, or an array of - strings). Failing that, the mode's helperType - property and finally the mode's name are used.
-
For example, the JavaScript mode has a - property fold containing "brace". When - the brace-fold addon is loaded, that defines a - helper named brace in the fold - namespace. This is then used by - the foldcode addon to - figure out that it can use that folding function to fold - JavaScript code.
-
When any 'global' - helpers are defined for the given namespace, their predicates - are called on the current mode and editor, and all those that - declare they are applicable will also be added to the array that - is returned.
- -
cm.getHelper(pos: {line, ch}, type: string) → helper
-
Returns the first applicable helper value. - See getHelpers.
- -
cm.getStateAfter(?line: integer, ?precise: boolean) → object
-
Returns the mode's parser state, if any, at the end of the - given line number. If no line number is given, the state at the - end of the document is returned. This can be useful for storing - parsing errors in the state, or getting other kinds of - contextual information for a line. precise is defined - as in getTokenAt().
-
- -

Miscellaneous methods

- -
-
cm.operation(func: () → any) → any
-
CodeMirror internally buffers changes and only updates its - DOM structure after it has finished performing some operation. - If you need to perform a lot of operations on a CodeMirror - instance, you can call this method with a function argument. It - will call the function, buffering up all changes, and only doing - the expensive update after the function returns. This can be a - lot faster. The return value from this method will be the return - value of your function.
- -
cm.startOperation()
-
cm.endOperation()
-
In normal circumstances, use the above operation - method. But if you want to buffer operations happening asynchronously, - or that can't all be wrapped in a callback function, you can - call startOperation to tell CodeMirror to start - buffering changes, and endOperation to actually - render all the updates. Be careful: if you use this - API and forget to call endOperation, the editor will - just never update.
- -
cm.indentLine(line: integer, ?dir: string|integer)
-
Adjust the indentation of the given line. The second - argument (which defaults to "smart") may be one of: -
-
"prev"
-
Base indentation on the indentation of the previous line.
-
"smart"
-
Use the mode's smart indentation if available, behave - like "prev" otherwise.
-
"add"
-
Increase the indentation of the line by - one indent unit.
-
"subtract"
-
Reduce the indentation of the line.
-
<integer>
-
Add (positive number) or reduce (negative number) the - indentation by the given amount of spaces.
-
- -
cm.toggleOverwrite(?value: boolean)
-
Switches between overwrite and normal insert mode (when not - given an argument), or sets the overwrite mode to a specific - state (when given an argument).
- -
cm.isReadOnly() → boolean
-
Tells you whether the editor's content can be edited by the - user.
- -
doc.lineSeparator()
-
Returns the preferred line separator string for this - document, as per the option - by the same name. When that option is null, the - string "\n" is returned.
- -
cm.execCommand(name: string)
-
Runs the command with the given name on the editor.
- -
doc.posFromIndex(index: integer) → {line, ch}
-
Calculates and returns a {line, ch} object for a - zero-based index who's value is relative to the start of the - editor's text. If the index is out of range of the text then - the returned object is clipped to start or end of the text - respectively.
-
doc.indexFromPos(object: {line, ch}) → integer
-
The reverse of posFromIndex.
- -
cm.focus()
-
Give the editor focus.
- -
cm.phrase(text: string) → string
-
Allow the given string to be translated with - the phrases - option.
- -
cm.getInputField() → Element
-
Returns the input field for the editor. Will be a textarea - or an editable div, depending on the value of - the inputStyle - option.
-
cm.getWrapperElement() → Element
-
Returns the DOM node that represents the editor, and - controls its size. Remove this from your tree to delete an - editor instance.
-
cm.getScrollerElement() → Element
-
Returns the DOM node that is responsible for the scrolling - of the editor.
-
cm.getGutterElement() → Element
-
Fetches the DOM node that contains the editor gutters.
-
- -

Static properties

-

The CodeMirror object itself provides - several useful properties.

- -
-
CodeMirror.version: string
-
It contains a string that indicates the version of the - library. This is a triple of - integers "major.minor.patch", - where patch is zero for releases, and something - else (usually one) for dev snapshots.
- -
CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)
-
This method provides another way to initialize an editor. It - takes a textarea DOM node as first argument and an optional - configuration object as second. It will replace the textarea - with a CodeMirror instance, and wire up the form of that - textarea (if any) to make sure the editor contents are put into - the textarea when the form is submitted. The text in the - textarea will provide the content for the editor. A CodeMirror - instance created this way has three additional methods: -
-
cm.save()
-
Copy the content of the editor into the textarea.
- -
cm.toTextArea()
-
Remove the editor, and restore the original textarea (with - the editor's current content). If you dynamically create and - destroy editors made with `fromTextArea`, without destroying - the form they are part of, you should make sure to call - `toTextArea` to remove the editor, or its `"submit"` handler - on the form will cause a memory leak.
- -
cm.getTextArea() → TextAreaElement
-
Returns the textarea that the instance was based on.
-
-
- -
CodeMirror.defaults: object
-
An object containing default values for - all options. You can assign to its - properties to modify defaults (though this won't affect editors - that have already been created).
- -
CodeMirror.defineExtension(name: string, value: any)
-
If you want to define extra methods in terms of the - CodeMirror API, it is possible to - use defineExtension. This will cause the given - value (usually a method) to be added to all CodeMirror instances - created from then on.
- -
CodeMirror.defineDocExtension(name: string, value: any)
-
Like defineExtension, - but the method will be added to the interface - for Doc objects instead.
- -
CodeMirror.defineOption(name: string, - default: any, updateFunc: function)
-
Similarly, defineOption can be used to define new options for - CodeMirror. The updateFunc will be called with the - editor instance and the new value when an editor is initialized, - and whenever the option is modified - through setOption.
- -
CodeMirror.defineInitHook(func: function)
-
If your extension just needs to run some - code whenever a CodeMirror instance is initialized, - use CodeMirror.defineInitHook. Give it a function as - its only argument, and from then on, that function will be called - (with the instance as argument) whenever a new CodeMirror instance - is initialized.
- -
CodeMirror.registerHelper(type: string, name: string, value: helper)
-
Registers a helper value with the given name in - the given namespace (type). This is used to define - functionality that may be looked up by mode. Will create (if it - doesn't already exist) a property on the CodeMirror - object for the given type, pointing to an object - that maps names to values. I.e. after - doing CodeMirror.registerHelper("hint", "foo", - myFoo), the value CodeMirror.hint.foo will - point to myFoo.
- -
CodeMirror.registerGlobalHelper(type: string, name: string, predicate: fn(mode, CodeMirror), value: helper)
-
Acts - like registerHelper, - but also registers this helper as 'global', meaning that it will - be included by getHelpers - whenever the given predicate returns true when - called with the local mode and editor.
- -
CodeMirror.Pos(line: integer, ?ch: integer, ?sticky: string)
-
A constructor for the objects that are used to represent - positions in editor documents. sticky defaults to - null, but can be set to "before" - or "after" to make the position explicitly - associate with the character before or after it.
- -
CodeMirror.changeEnd(change: object) → {line, ch}
-
Utility function that computes an end position from a change - (an object with from, to, - and text properties, as passed to - various event handlers). The - returned position will be the end of the changed - range, after the change is applied.
- -
CodeMirror.countColumn(line: string, index: number, tabSize: number) → number
-
Find the column position at a given string index using a given tabsize.
-
-
- -
-

Addons

- -

The addon directory in the distribution contains a - number of reusable components that implement extra editor - functionality (on top of extension functions - like defineOption, defineExtension, - and registerHelper). In - brief, they are:

- -
-
dialog/dialog.js
-
Provides a very simple way to query users for text input. - Adds the openDialog(template, callback, options) → - closeFunction method to CodeMirror instances, - which can be called with an HTML fragment or a detached DOM - node that provides the prompt (should include an input - or button tag), and a callback function that is called - when the user presses enter. It returns a function closeFunction - which, if called, will close the dialog immediately. - openDialog takes the following options: -
-
closeOnEnter: bool
-
If true, the dialog will be closed when the user presses - enter in the input. Defaults to true.
-
closeOnBlur: bool
-
Determines whether the dialog is closed when it loses focus. Defaults to true.
-
onKeyDown: fn(event: KeyboardEvent, value: string, close: fn()) → bool
-
An event handler that will be called whenever keydown fires in the - dialog's input. If your callback returns true, - the dialog will not do any further processing of the event.
-
onKeyUp: fn(event: KeyboardEvent, value: string, close: fn()) → bool
-
Same as onKeyDown but for the - keyup event.
-
onInput: fn(event: InputEvent, value: string, close: fn()) → bool
-
Same as onKeyDown but for the - input event.
-
onClose: fn(instance):
-
A callback that will be called after the dialog has been closed and - removed from the DOM. No return value.
-
- -

Also adds an openNotification(template, options) → - closeFunction function that simply shows an HTML - fragment as a notification at the top of the editor. It takes a - single option: duration, the amount of time after - which the notification will be automatically closed. If - duration is zero, the dialog will not be closed automatically.

- -

Depends on addon/dialog/dialog.css.

- -
search/searchcursor.js
-
Adds the getSearchCursor(query, start, options) → - cursor method to CodeMirror instances, which can be used - to implement search/replace functionality. query - can be a regular expression or a string. start - provides the starting position of the search. It can be - a {line, ch} object, or can be left off to default - to the start of the document. options is an - optional object, which can contain the property `caseFold: - false` to disable case folding when matching a string, or the - property `multiline: disable` to disable multi-line matching for - regular expressions (which may help performance). A search - cursor has the following methods: -
-
findNext() → boolean
-
findPrevious() → boolean
-
Search forward or backward from the current position. - The return value indicates whether a match was found. If - matching a regular expression, the return value will be the - array returned by the match method, in case you - want to extract matched groups.
-
from() → {line, ch}
-
to() → {line, ch}
-
These are only valid when the last call - to findNext or findPrevious did - not return false. They will return {line, ch} - objects pointing at the start and end of the match.
-
replace(text: string, ?origin: string)
-
Replaces the currently found match with the given text - and adjusts the cursor position to reflect the - replacement.
-
- - -
Implements the search commands. CodeMirror has keys bound to - these by default, but will not do anything with them unless an - implementation is provided. Depends - on searchcursor.js, and will make use - of openDialog when - available to make prompting for search queries less ugly.
- -
search/jump-to-line.js
-
Implements a jumpToLine command and binding Alt-G to it. - Accepts linenumber, +/-linenumber, line:char, - scroll% and :linenumber formats. - This will make use of openDialog - when available to make prompting for line number neater.
- -
search/matchesonscrollbar.js
-
Adds a showMatchesOnScrollbar method to editor - instances, which should be given a query (string or regular - expression), optionally a case-fold flag (only applicable for - strings), and optionally a class name (defaults - to CodeMirror-search-match) as arguments. When - called, matches of the given query will be displayed on the - editor's vertical scrollbar. The method returns an object with - a clear method that can be called to remove the - matches. Depends on - the annotatescrollbar - addon, and - the matchesonscrollbar.css - file provides a default (transparent yellowish) definition of - the CSS class applied to the matches. Note that the matches are - only perfectly aligned if your scrollbar does not have buttons - at the top and bottom. You can use - the simplescrollbar - addon to make sure of this. If this addon is loaded, - the search addon will - automatically use it.
- -
edit/matchbrackets.js
-
Defines an option matchBrackets which, when set - to true or an options object, causes matching brackets to be - highlighted whenever the cursor is next to them. It also adds a - method matchBrackets that forces this to happen - once, and a method findMatchingBracket that can be - used to run the bracket-finding algorithm that this uses - internally. It takes a start position and an optional config - object. By default, it will find the match to a matchable - character either before or after the cursor (preferring the one - before), but you can control its behavior with these options: -
-
afterCursor
-
Only use the character after the start position, never the one before it.
-
strict
-
Causes only matches where both brackets are at the same side of the start position to be considered.
-
maxScanLines
-
Stop after scanning this amount of lines without a successful match. Defaults to 1000.
-
maxScanLineLength
-
Ignore lines longer than this. Defaults to 10000.
-
maxHighlightLineLength
-
Don't highlight a bracket in a line longer than this. Defaults to 1000.
-
- -
edit/closebrackets.js
-
Defines an option autoCloseBrackets that will - auto-close brackets and quotes when typed. By default, it'll - auto-close ()[]{}''"", but you can pass it a string - similar to that (containing pairs of matching characters), or an - object with pairs and - optionally explode properties to customize - it. explode should be a similar string that gives - the pairs of characters that, when enter is pressed between - them, should have the second character also moved to its own - line. By default, if the active mode has - a closeBrackets property, that overrides the - configuration given in the option. But you can add - an override property with a truthy value to - override mode-specific - configuration. Demo - here.
- -
edit/matchtags.js
-
Defines an option matchTags that, when enabled, - will cause the tags around the cursor to be highlighted (using - the CodeMirror-matchingtag class). Also - defines - a command toMatchingTag, - which you can bind a key to in order to jump to the tag matching - the one under the cursor. Depends on - the addon/fold/xml-fold.js - addon. Demo here.
- -
edit/trailingspace.js
-
Adds an option showTrailingSpace which, when - enabled, adds the CSS class cm-trailingspace to - stretches of whitespace at the end of lines. - The demo has a nice - squiggly underline style for this class.
- -
edit/closetag.js
-
Defines an autoCloseTags option that will - auto-close XML tags when '>' or '/' - is typed, and - a closeTag command that - closes the nearest open tag. Depends on - the fold/xml-fold.js addon. See - the demo.
- -
edit/continuelist.js
-
Markdown specific. Defines - a "newlineAndIndentContinueMarkdownList" command - that can be bound to enter to automatically - insert the leading characters for continuing a list. See - the Markdown mode - demo.
- -
comment/comment.js
-
Addon for commenting and uncommenting code. Adds four - methods to CodeMirror instances: -
-
toggleComment(?options: object)
-
Tries to uncomment the current selection, and if that - fails, line-comments it.
-
lineComment(from: {line, ch}, to: {line, ch}, ?options: object)
-
Set the lines in the given range to be line comments. Will - fall back to blockComment when no line comment - style is defined for the mode.
-
blockComment(from: {line, ch}, to: {line, ch}, ?options: object)
-
Wrap the code in the given range in a block comment. Will - fall back to lineComment when no block comment - style is defined for the mode.
-
uncomment(from: {line, ch}, to: {line, ch}, ?options: object) → boolean
-
Try to uncomment the given range. - Returns true if a comment range was found and - removed, false otherwise.
-
- The options object accepted by these methods may - have the following properties: -
-
blockCommentStart, blockCommentEnd, blockCommentLead, lineComment: string
-
Override the comment string - properties of the mode with custom comment strings.
-
padding: string
-
A string that will be inserted after opening and leading - markers, and before closing comment markers. Defaults to a - single space.
-
commentBlankLines: boolean
-
Whether, when adding line comments, to also comment lines - that contain only whitespace.
-
indent: boolean
-
When adding line comments and this is turned on, it will - align the comment block to the current indentation of the - first line of the block.
-
fullLines: boolean
-
When block commenting, this controls whether the whole - lines are indented, or only the precise range that is given. - Defaults to true.
-
- The addon also defines - a toggleComment command, - which is a shorthand command for calling - toggleComment with no options.
- -
fold/foldcode.js
-
Helps with code folding. Adds a foldCode method - to editor instances, which will try to do a code fold starting - at the given line, or unfold the fold that is already present. - The method takes as first argument the position that should be - folded (may be a line number or - a Pos), and as second optional - argument either a range-finder function, or an options object, - supporting the following properties: -
-
rangeFinder: fn(CodeMirror, Pos)
-
The function that is used to find - foldable ranges. If this is not directly passed, it will - default to CodeMirror.fold.auto, which - uses getHelpers with - a "fold" type to find folding functions - appropriate for the local mode. There are files in - the addon/fold/ - directory providing CodeMirror.fold.brace, which - finds blocks in brace languages (JavaScript, C, Java, - etc), CodeMirror.fold.indent, for languages where - indentation determines block structure (Python, Haskell), - and CodeMirror.fold.xml, for XML-style languages, - and CodeMirror.fold.comment, for folding comment - blocks.
-
widget: string | Element | fn(from: Pos, to: Pos) → string|Element
-
The widget to show for folded ranges. Can be either a - string, in which case it'll become a span with - class CodeMirror-foldmarker, or a DOM node. - To dynamically generate the widget, this can be a function - that returns a string or DOM node, which will then render - as described. The function will be invoked with parameters - identifying the range to be folded.
-
scanUp: boolean
-
When true (default is false), the addon will try to find - foldable ranges on the lines above the current one if there - isn't an eligible one on the given line.
-
minFoldSize: integer
-
The minimum amount of lines that a fold should span to be - accepted. Defaults to 0, which also allows single-line - folds.
-
- See the demo for an - example.
- -
fold/foldgutter.js
-
Provides an option foldGutter, which can be - used to create a gutter with markers indicating the blocks that - can be folded. Create a gutter using - the gutters option, - giving it the class CodeMirror-foldgutter or - something else if you configure the addon to use a different - class, and this addon will show markers next to folded and - foldable blocks, and handle clicks in this gutter. Note that - CSS styles should be applied to make the gutter, and the fold - markers within it, visible. A default set of CSS styles are - available in: - - addon/fold/foldgutter.css - . - The option - can be either set to true, or an object containing - the following optional option fields: -
-
gutter: string
-
The CSS class of the gutter. Defaults - to "CodeMirror-foldgutter". You will have to - style this yourself to give it a width (and possibly a - background). See the default gutter style rules above.
-
indicatorOpen: string | Element
-
A CSS class or DOM element to be used as the marker for - open, foldable blocks. Defaults - to "CodeMirror-foldgutter-open".
-
indicatorFolded: string | Element
-
A CSS class or DOM element to be used as the marker for - folded blocks. Defaults to "CodeMirror-foldgutter-folded".
-
rangeFinder: fn(CodeMirror, Pos)
-
The range-finder function to use when determining whether - something can be folded. When not - given, CodeMirror.fold.auto - will be used as default.
-
- The foldOptions editor option can be set to an - object to provide an editor-wide default configuration. - Demo here.
- -
runmode/runmode.js
-
Can be used to run a CodeMirror mode over text without - actually opening an editor instance. - See the demo for an example. - There are alternate versions of the file available for - running stand-alone - (without including all of CodeMirror) and - for running under - node.js (see bin/source-highlight for an example of using the latter).
- -
runmode/colorize.js
-
Provides a convenient way to syntax-highlight code snippets - in a webpage. Depends on - the runmode addon (or - its standalone variant). Provides - a CodeMirror.colorize function that can be called - with an array (or other array-ish collection) of DOM nodes that - represent the code snippets. By default, it'll get - all pre tags. Will read the data-lang - attribute of these nodes to figure out their language, and - syntax-color their content using the relevant CodeMirror mode - (you'll have to load the scripts for the relevant modes - yourself). A second argument may be provided to give a default - mode, used when no language attribute is found for a node. Used - in this manual to highlight example code.
- -
mode/overlay.js
-
Mode combinator that can be used to extend a mode with an - 'overlay' — a secondary mode is run over the stream, along with - the base mode, and can color specific pieces of text without - interfering with the base mode. - Defines CodeMirror.overlayMode, which is used to - create such a mode. See this - demo for a detailed example.
- -
mode/multiplex.js
-
Mode combinator that can be used to easily 'multiplex' - between several modes. - Defines CodeMirror.multiplexingMode which, when - given as first argument a mode object, and as other arguments - any number of {open, close, mode [, delimStyle, innerStyle, parseDelimiters]} - objects, will return a mode object that starts parsing using the - mode passed as first argument, but will switch to another mode - as soon as it encounters a string that occurs in one of - the open fields of the passed objects. When in a - sub-mode, it will go back to the top mode again when - the close string is encountered. - Pass "\n" for open or close - if you want to switch on a blank line. -
  • When delimStyle is specified, it will be the token - style returned for the delimiter tokens (as well as - [delimStyle]-open on the opening token and - [delimStyle]-close on the closing token).
  • -
  • When innerStyle is specified, it will be the token - style added for each inner mode token.
  • -
  • When parseDelimiters is true, the content of - the delimiters will also be passed to the inner mode. - (And delimStyle is ignored.)
The outer - mode will not see the content between the delimiters. - See this demo for an - example.
- -
hint/show-hint.js
-
Provides a framework for showing autocompletion hints. - Defines editor.showHint, which takes an optional - options object, and pops up a widget that allows the user to - select a completion. Finding hints is done with a hinting - function (the hint option). This function - takes an editor instance and an options object, and returns - a {list, from, to} object, where list - is an array of strings or objects (the completions), - and from and to give the start and end - of the token that is being completed as {line, ch} - objects. An optional selectedHint property (an - integer) can be added to the completion object to control the - initially selected hint.
-
If no hinting function is given, the addon will - use CodeMirror.hint.auto, which - calls getHelpers with - the "hint" type to find applicable hinting - functions, and tries them one by one. If that fails, it looks - for a "hintWords" helper to fetch a list of - completable words for the mode, and - uses CodeMirror.hint.fromList to complete from - those.
-
When completions aren't simple strings, they should be - objects with the following properties: -
-
text: string
-
The completion text. This is the only required - property.
-
displayText: string
-
The text that should be displayed in the menu.
-
className: string
-
A CSS class name to apply to the completion's line in the - menu.
-
render: fn(Element, self, data)
-
A method used to create the DOM structure for showing the - completion by appending it to its first argument.
-
hint: fn(CodeMirror, self, data)
-
A method used to actually apply the completion, instead of - the default behavior.
-
from: {line, ch}
-
Optional from position that will be used by pick() instead - of the global one passed with the full list of completions.
-
to: {line, ch}
-
Optional to position that will be used by pick() instead - of the global one passed with the full list of completions.
-
- -
The plugin understands the following options, which may be - either passed directly in the argument to showHint, - or provided by setting an hintOptions editor - option to an object (the former takes precedence). The options - object will also be passed along to the hinting function, which - may understand additional options. -
-
hint: function
-
A hinting function, as specified above. It is possible to - set the async property on a hinting function to - true, in which case it will be called with - arguments (cm, callback, ?options), and the - completion interface will only be popped up when the hinting - function calls the callback, passing it the object holding the - completions. - The hinting function can also return a promise, and the completion - interface will only be popped when the promise resolves. - By default, hinting only works when there is no - selection. You can give a hinting function - a supportsSelection property with a truthy value - to indicate that it supports selections.
-
completeSingle: boolean
-
Determines whether, when only a single completion is - available, it is completed without showing the dialog. - Defaults to true.
-
alignWithWord: boolean
-
Whether the pop-up should be horizontally aligned with the - start of the word (true, default), or with the cursor (false).
-
closeCharacters: RegExp
-
A regular expression object used to match characters which - cause the pop up to be closed (default: /[\s()\[\]{};:>,]/). - If the user types one of these characters, the pop up will close, and - the endCompletion event is fired on the editor instance.
-
closeOnUnfocus: boolean
-
When enabled (which is the default), the pop-up will close - when the editor is unfocused.
-
completeOnSingleClick: boolean
-
Whether a single click on a list item suffices to trigger the - completion (which is the default), or if the user has to use a - doubleclick.
-
container: Element|null
-
Can be used to define a custom container for the widget. The default - is null, in which case the body-element will - be used.
-
customKeys: keymap
-
Allows you to provide a custom key map of keys to be active - when the pop-up is active. The handlers will be called with an - extra argument, a handle to the completion menu, which - has moveFocus(n), setFocus(n), pick(), - and close() methods (see the source for details), - that can be used to change the focused element, pick the - current element or close the menu. Additionally menuSize() - can give you access to the size of the current dropdown menu, - length give you the number of available completions, and - data give you full access to the completion returned by the - hinting function.
-
extraKeys: keymap
-
Like customKeys above, but the bindings will - be added to the set of default bindings, instead of replacing - them.
-
scrollMargin: integer
-
Show this many lines before and after the selected item. - Default is 0.
-
- The following events will be fired on the completions object - during completion: -
-
"shown" ()
-
Fired when the pop-up is shown.
-
"select" (completion, Element)
-
Fired when a completion is selected. Passed the completion - value (string or object) and the DOM node that represents it - in the menu.
-
"pick" (completion)
-
Fired when a completion is picked. Passed the completion value - (string or object).
-
"close" ()
-
Fired when the completion is finished.
-
- The following events will be fired on the editor instance during - completion: -
-
"endCompletion" ()
-
Fired when the pop-up is being closed programmatically, e.g., when - the user types a character which matches the - closeCharacters option.
-
- This addon depends on styles - from addon/hint/show-hint.css. Check - out the demo for an - example.
- -
hint/javascript-hint.js
-
Defines a simple hinting function for JavaScript - (CodeMirror.hint.javascript) and CoffeeScript - (CodeMirror.hint.coffeescript) code. This will - simply use the JavaScript environment that the editor runs in as - a source of information about objects and their properties.
- -
hint/xml-hint.js
-
Defines CodeMirror.hint.xml, which produces - hints for XML tagnames, attribute names, and attribute values, - guided by a schemaInfo option (a property of the - second argument passed to the hinting function, or the third - argument passed to CodeMirror.showHint).
The - schema info should be an object mapping tag names to information - about these tags, with optionally a "!top" property - containing a list of the names of valid top-level tags. The - values of the properties should be objects with optional - properties children (an array of valid child - element names, omit to simply allow all tags to appear) - and attrs (an object mapping attribute names - to null for free-form attributes, and an array of - valid values for restricted - attributes).
The hint options accept an additional property: -
-
matchInMiddle: boolean
-
Determines whether typed characters are matched anywhere in - completions, not just at the beginning. Defaults to false.
-
- Demo here.
- -
hint/html-hint.js
-
Provides schema info to - the xml-hint addon for HTML - documents. Defines a schema - object CodeMirror.htmlSchema that you can pass to - as a schemaInfo option, and - a CodeMirror.hint.html hinting function that - automatically calls CodeMirror.hint.xml with this - schema data. See - the demo.
- -
hint/css-hint.js
-
A hinting function for CSS, SCSS, or LESS code. - Defines CodeMirror.hint.css.
- -
hint/anyword-hint.js
-
A very simple hinting function - (CodeMirror.hint.anyword) that simply looks for - words in the nearby code and completes to those. Takes two - optional options, word, a regular expression that - matches words (sequences of one or more character), - and range, which defines how many lines the addon - should scan when completing (defaults to 500).
- -
hint/sql-hint.js
-
A simple SQL hinter. Defines CodeMirror.hint.sql. - Takes two optional options, tables, a object with - table names as keys and array of respective column names as values, - and defaultTable, a string corresponding to a - table name in tables for autocompletion.
- -
search/match-highlighter.js
-
Adds a highlightSelectionMatches option that - can be enabled to highlight all instances of a currently - selected word. Can be set either to true or to an object - containing the following options: minChars, for the - minimum amount of selected characters that triggers a highlight - (default 2), style, for the style to be used to - highlight the matches (default "matchhighlight", - which will correspond to CSS - class cm-matchhighlight), trim, which - controls whether whitespace is trimmed from the selection, - and showToken which can be set to true - or to a regexp matching the characters that make up a word. When - enabled, it causes the current word to be highlighted when - nothing is selected (defaults to off). - Demo here.
- -
lint/lint.js
-
Defines an interface component for showing linting warnings, - with pluggable warning sources - (see html-lint.js, - json-lint.js, - javascript-lint.js, - coffeescript-lint.js, - and css-lint.js - in the same directory). Defines a lint option that - can be set to an annotation source (for - example CodeMirror.lint.javascript), to an options - object (in which case the getAnnotations field is - used as annotation source), or simply to true. When - no annotation source is - specified, getHelper with - type "lint" is used to find an annotation function. - An annotation source function should, when given a document - string, an options object, and an editor instance, return an - array of {message, severity, from, to} objects - representing problems. When the function has - an async property with a truthy value, it will be - called with an additional second argument, which is a callback - to pass the array to. - The linting function can also return a promise, in that case the linter - will only be executed when the promise resolves. - By default, the linter will run (debounced) whenever the document is changed. - You can pass a lintOnChange: false option to disable that. - You can pass a selfContain: true option to render the tooltip inside the editor instance. - Depends on addon/lint/lint.css. A demo can be - found here.
- -
selection/mark-selection.js
-
Causes the selected text to be marked with the CSS class - CodeMirror-selectedtext when the styleSelectedText option - is enabled. Useful to change the colour of the selection (in addition to the background), - like in this demo.
- -
selection/active-line.js
-
Defines a styleActiveLine option that, when - enabled, gives the wrapper of the line that contains the cursor - the class CodeMirror-activeline, adds a background - with the class CodeMirror-activeline-background, - and adds the class CodeMirror-activeline-gutter to - the line's gutter space is enabled. The option's value may be a - boolean or an object specifying the following options: -
-
nonEmpty: bool
-
Controls whether single-line selections, or just cursor - selections, are styled. Defaults to false (only cursor - selections).
-
- See the demo.
- -
selection/selection-pointer.js
-
Defines a selectionPointer option which you can - use to control the mouse cursor appearance when hovering over - the selection. It can be set to a string, - like "pointer", or to true, in which case - the "default" (arrow) cursor will be used. You can - see a demo here.
- -
mode/loadmode.js
-
Defines a CodeMirror.requireMode(modename, callback, - options) function that will try to load a given mode and - call the callback when it succeeded. options is an - optional object that may contain: -
-
path: fn(modeName: string) → string
-
Defines the way mode names are mapped to paths.
-
loadMode: fn(path: string, cont: fn())
-
Override the way the mode script is loaded. By default, - this will use the CommonJS or AMD module loader if one is - present, and fall back to creating - a <script> tag otherwise.
-
- This addon also - defines CodeMirror.autoLoadMode(instance, mode), - which will ensure the given mode is loaded and cause the given - editor instance to refresh its mode when the loading - succeeded. See the demo.
- -
mode/meta.js
-
Provides meta-information about all the modes in the - distribution in a single file. - Defines CodeMirror.modeInfo, an array of objects - with {name, mime, mode} properties, - where name is the human-readable - name, mime the MIME type, and mode the - name of the mode file that defines this MIME. There are optional - properties mimes, which holds an array of MIME - types for modes with multiple MIMEs associated, - and ext, which holds an array of file extensions - associated with this mode. Four convenience - functions, CodeMirror.findModeByMIME, - CodeMirror.findModeByExtension, - CodeMirror.findModeByFileName - and CodeMirror.findModeByName are provided, which - return such an object given a MIME, extension, file name or mode name - string. Note that, for historical reasons, this file resides in the - top-level mode directory, not - under addon. Demo.
- -
comment/continuecomment.js
-
Adds a continueComments option, which sets whether the - editor will make the next line continue a comment when you press Enter - inside a comment block. Can be set to a boolean to enable/disable this - functionality. Set to a string, it will continue comments using a custom - shortcut. Set to an object, it will use the key property for - a custom shortcut and the boolean continueLineComment - property to determine whether single-line comments should be continued - (defaulting to true).
- -
display/placeholder.js
-
Adds a placeholder option that can be used to - make content appear in the editor when it is empty and not - focused. It can hold either a string or a DOM node. Also gives - the editor a CodeMirror-empty CSS class whenever it - doesn't contain any text. - See the demo.
- -
display/fullscreen.js
-
Defines an option fullScreen that, when set - to true, will make the editor full-screen (as in, - taking up the whole browser window). Depends - on fullscreen.css. Demo - here.
- -
display/autorefresh.js
-
This addon can be useful when initializing an editor in a - hidden DOM node, in cases where it is difficult to - call refresh when the editor - becomes visible. It defines an option autoRefresh - which you can set to true to ensure that, if the editor wasn't - visible on initialization, it will be refreshed the first time - it becomes visible. This is done by polling every 250 - milliseconds (you can pass a value like {delay: - 500} as the option value to configure this). Note that - this addon will only refresh the editor once when it - first becomes visible, and won't take care of further restyling - and resizing.
- -
scroll/simplescrollbars.js
-
Defines two additional scrollbar - models, "simple" and "overlay" - (see demo) that can - be selected with - the scrollbarStyle - option. Depends - on simplescrollbars.css, - which can be further overridden to style your own - scrollbars.
- -
scroll/annotatescrollbar.js
-
Provides functionality for showing markers on the scrollbar - to call out certain parts of the document. Adds a - method annotateScrollbar to editor instances that - can be called, with a CSS class name as argument, to create a - set of annotations. The method returns an object - whose update method can be called with a sorted array - of {from: Pos, to: Pos} objects marking the ranges - to be highlighted. To detach the annotations, call the - object's clear method.
- -
display/rulers.js
-
Adds a rulers option, which can be used to show - one or more vertical rulers in the editor. The option, if - defined, should be given an array of {column [, className, - color, lineStyle, width]} objects or numbers (which - indicate a column). The ruler will be displayed at the column - indicated by the number or the column property. - The className property can be used to assign a - custom style to a ruler. Demo - here.
- -
display/panel.js
-
Defines an addPanel method for CodeMirror - instances, which places a DOM node above or below an editor, and - shrinks the editor to make room for the node. The method takes - as first argument as DOM node, and as second an optional options - object. The Panel object returned by this method - has a clear method that is used to remove the - panel, and a changed method that can be used to - notify the addon when the size of the panel's DOM node has - changed.
- The method accepts the following options: -
-
position: string
-
Controls the position of the newly added panel. The - following values are recognized: -
-
top (default)
-
Adds the panel at the very top.
-
after-top
-
Adds the panel at the bottom of the top panels.
-
bottom
-
Adds the panel at the very bottom.
-
before-bottom
-
Adds the panel at the top of the bottom panels.
-
-
-
before: Panel
-
The new panel will be added before the given panel.
-
after: Panel
-
The new panel will be added after the given panel.
-
replace: Panel
-
The new panel will replace the given panel.
-
stable: bool
-
Whether to scroll the editor to keep the text's vertical - position stable, when adding a panel above it. Defaults to false.
-
- When using the after, before or replace options, - if the panel doesn't exists or has been removed, - the value of the position option will be used as a fallback. -
- A demo of the addon is available here. -
- -
wrap/hardwrap.js
-
Addon to perform hard line wrapping/breaking for paragraphs - of text. Adds these methods to editor instances: -
-
wrapParagraph(?pos: {line, ch}, ?options: object)
-
Wraps the paragraph at the given position. - If pos is not given, it defaults to the cursor - position.
-
wrapRange(from: {line, ch}, to: {line, ch}, ?options: object)
-
Wraps the given range as one big paragraph.
-
wrapParagraphsInRange(from: {line, ch}, to: {line, ch}, ?options: object)
-
Wraps the paragraphs in (and overlapping with) the - given range individually.
-
- The following options are recognized: -
-
paragraphStart, paragraphEnd: RegExp
-
Blank lines are always considered paragraph boundaries. - These options can be used to specify a pattern that causes - lines to be considered the start or end of a paragraph.
-
column: number
-
The column to wrap at. Defaults to 80.
-
wrapOn: RegExp
-
A regular expression that matches only those - two-character strings that allow wrapping. By default, the - addon wraps on whitespace and after dash characters.
-
killTrailingSpace: boolean
-
Whether trailing space caused by wrapping should be - preserved, or deleted. Defaults to true.
-
forceBreak: boolean
-
If set to true forces a break at column in the case - when no wrapOn pattern is found in the range. If set to - false allows line to overflow the column limit if no - wrapOn pattern found. Defaults to true.
-
- A demo of the addon is available here. -
- -
scroll/scrollpastend.js
-
Defines an option `"scrollPastEnd"` that, when set to a - truthy value, allows the user to scroll one editor height of - empty space into view at the bottom of the editor.
- -
merge/merge.js
-
Implements an interface for merging changes, using either a - 2-way or a 3-way view. The CodeMirror.MergeView - constructor takes arguments similar to - the CodeMirror - constructor, first a node to append the interface to, and then - an options object. Options are passed through to the editors - inside the view. These extra options are recognized: -
-
origLeft and origRight: string
-
If given these provide original versions of the - document, which will be shown to the left and right of the - editor in non-editable CodeMirror instances. The merge - interface will highlight changes between the editable - document and the original(s). To create a 2-way (as opposed - to 3-way) merge view, provide only one of them.
-
revertButtons: boolean
-
Determines whether buttons that allow the user to revert - changes are shown. Defaults to true.
-
revertChunk: fn(mv: MergeView, from: CodeMirror, fromStart: Pos, fromEnd: Pos, to: CodeMirror, toStart: Pos, toEnd: Pos)
-
Can be used to define custom behavior when the user - reverts a changed chunk.
-
connect: string
-
Sets the style used to connect changed chunks of code. - By default, connectors are drawn. When this is set - to "align", the smaller chunk is padded to - align with the bigger chunk instead.
-
collapseIdentical: boolean|number
-
When true (default is false), stretches of unchanged - text will be collapsed. When a number is given, this - indicates the amount of lines to leave visible around such - stretches (which defaults to 2).
-
allowEditingOriginals: boolean
-
Determines whether the original editor allows editing. - Defaults to false.
-
showDifferences: boolean
-
When true (the default), changed pieces of text are - highlighted.
-
chunkClassLocation: string|Array
-
By default the chunk highlights are added - using addLineClass - with "background". Override this to customize it to be any - valid `where` parameter or an Array of valid `where` - parameters.
-
- The addon also defines commands "goNextDiff" - and "goPrevDiff" to quickly jump to the next - changed chunk. Demo - here.
- -
tern/tern.js
-
Provides integration with - the Tern JavaScript analysis - engine, for completion, definition finding, and minor - refactoring help. See the demo - for a very simple integration. For more involved scenarios, see - the comments at the top of - the addon and the - implementation of the - (multi-file) demonstration - on the Tern website.
-
-
- -
-

Writing CodeMirror Modes

- -

Modes typically consist of a single JavaScript file. This file - defines, in the simplest case, a lexer (tokenizer) for your - language—a function that takes a character stream as input, - advances it past a token, and returns a style for that token. More - advanced modes can also handle indentation for the language.

- -

This section describes the low-level mode interface. Many modes - are written directly against this, since it offers a lot of - control, but for a quick mode definition, you might want to use - the simple mode addon.

- -

The mode script should - call CodeMirror.defineMode to - register itself with CodeMirror. This function takes two - arguments. The first should be the name of the mode, for which you - should use a lowercase string, preferably one that is also the - name of the files that define the mode (i.e. "xml" is - defined in xml.js). The second argument should be a - function that, given a CodeMirror configuration object (the thing - passed to the CodeMirror function) and an optional - mode configuration object (as in - the mode option), returns - a mode object.

- -

Typically, you should use this second argument - to defineMode as your module scope function (modes - should not leak anything into the global scope!), i.e. write your - whole mode inside this function.

- -

The main responsibility of a mode script is parsing - the content of the editor. Depending on the language and the - amount of functionality desired, this can be done in really easy - or extremely complicated ways. Some parsers can be stateless, - meaning that they look at one element (token) of the code - at a time, with no memory of what came before. Most, however, will - need to remember something. This is done by using a state - object, which is an object that is always passed when - reading a token, and which can be mutated by the tokenizer.

- -

Modes that use a state must define - a startState method on their mode - object. This is a function of no arguments that produces a state - object to be used at the start of a document.

- -

The most important part of a mode object is - its token(stream, state) method. All - modes must define this method. It should read one token from the - stream it is given as an argument, optionally update its state, - and return a style string, or null for tokens that do - not have to be styled. For your styles, you are encouraged to use - the 'standard' names defined in the themes (without - the cm- prefix). If that fails, it is also possible - to come up with your own and write your own CSS theme file.

- -

A typical token string would - be "variable" or "comment". Multiple - styles can be returned (separated by spaces), for - example "string error" for a thing that looks like a - string but is invalid somehow (say, missing its closing quote). - When a style is prefixed by "line-" - or "line-background-", the style will be applied to - the whole line, analogous to what - the addLineClass method - does—styling the "text" in the simple case, and - the "background" element - when "line-background-" is prefixed.

- -

The stream object that's passed - to token encapsulates a line of code (tokens may - never span lines) and our current position in that line. It has - the following API:

- -
-
eol() → boolean
-
Returns true only if the stream is at the end of the - line.
-
sol() → boolean
-
Returns true only if the stream is at the start of the - line.
- -
peek() → string
-
Returns the next character in the stream without advancing - it. Will return a null at the end of the - line.
-
next() → string
-
Returns the next character in the stream and advances it. - Also returns null when no more characters are - available.
- -
eat(match: string|regexp|function(char: string) → boolean) → string
-
match can be a character, a regular expression, - or a function that takes a character and returns a boolean. If - the next character in the stream 'matches' the given argument, - it is consumed and returned. Otherwise, undefined - is returned.
-
eatWhile(match: string|regexp|function(char: string) → boolean) → boolean
-
Repeatedly calls eat with the given argument, - until it fails. Returns true if any characters were eaten.
-
eatSpace() → boolean
-
Shortcut for eatWhile when matching - white-space.
-
skipToEnd()
-
Moves the position to the end of the line.
-
skipTo(str: string) → boolean
-
Skips to the start of the next occurrence of the given string, if - found on the current line (doesn't advance the stream if the - string does not occur on the line). Returns true if the - string was found.
-
match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
-
match(pattern: regexp, ?consume: boolean) → array<string>
-
Act like a - multi-character eat—if consume is true - or not given—or a look-ahead that doesn't update the stream - position—if it is false. pattern can be either a - string or a regular expression starting with ^. - When it is a string, caseFold can be set to true to - make the match case-insensitive. When successfully matching a - regular expression, the returned value will be the array - returned by match, in case you need to extract - matched groups.
- -
backUp(n: integer)
-
Backs up the stream n characters. Backing it up - further than the start of the current token will cause things to - break, so be careful.
-
column() → integer
-
Returns the column (taking into account tabs) at which the - current token starts.
-
indentation() → integer
-
Tells you how far the current line has been indented, in - spaces. Corrects for tab characters.
- -
current() → string
-
Get the string between the start of the current token and - the current stream position.
- -
lookAhead(n: number) → ?string
-
Get the line n (>0) lines after the current - one, in order to scan ahead across line boundaries. Note that - you want to do this carefully, since looking far ahead will make - mode state caching much less effective.
- -
baseToken() → ?{type: ?string, size: number}
-
Modes added - through addOverlay - (and only such modes) can use this method to inspect - the current token produced by the underlying mode.
-
- -

By default, blank lines are simply skipped when - tokenizing a document. For languages that have significant blank - lines, you can define - a blankLine(state) method on your - mode that will get called whenever a blank line is passed over, so - that it can update the parser state.

- -

Because state object are mutated, and CodeMirror - needs to keep valid versions of a state around so that it can - restart a parse at any line, copies must be made of state objects. - The default algorithm used is that a new state object is created, - which gets all the properties of the old object. Any properties - which hold arrays get a copy of these arrays (since arrays tend to - be used as mutable stacks). When this is not correct, for example - because a mode mutates non-array properties of its state object, a - mode object should define - a copyState method, which is given a - state and should return a safe copy of that state.

- -

If you want your mode to provide smart indentation - (through the indentLine - method and the indentAuto - and newlineAndIndent commands, to which keys can be - bound), you must define - an indent(state, textAfter) method - on your mode object.

- -

The indentation method should inspect the given state object, - and optionally the textAfter string, which contains - the text on the line that is being indented, and return an - integer, the amount of spaces to indent. It should usually take - the indentUnit - option into account. An indentation method may - return CodeMirror.Pass to indicate that it - could not come up with a precise indentation.

- -

To work well with - the commenting addon, a mode may - define lineComment (string that - starts a line - comment), blockCommentStart, blockCommentEnd - (strings that start and end block comments), - and blockCommentLead (a string to put at the start of - continued lines in a block comment). All of these are - optional.

- -

Finally, a mode may define either - an electricChars or an electricInput - property, which are used to automatically reindent the line when - certain patterns are typed and - the electricChars - option is enabled. electricChars may be a string, and - will trigger a reindent whenever one of the characters in that - string are typed. Often, it is more appropriate to - use electricInput, which should hold a regular - expression, and will trigger indentation when the part of the - line before the cursor matches the expression. It should - usually end with a $ character, so that it only - matches when the indentation-changing pattern was just typed, not when something was - typed after the pattern.

- -

So, to summarize, a mode must provide - a token method, and it may - provide startState, copyState, - and indent methods. For an example of a trivial mode, - see the diff mode, for a more - involved example, see the C-like - mode.

- -

Sometimes, it is useful for modes to nest—to have one - mode delegate work to another mode. An example of this kind of - mode is the mixed-mode HTML - mode. To implement such nesting, it is usually necessary to - create mode objects and copy states yourself. To create a mode - object, there are CodeMirror.getMode(options, - parserConfig), where the first argument is a configuration - object as passed to the mode constructor function, and the second - argument is a mode specification as in - the mode option. To copy a - state object, call CodeMirror.copyState(mode, state), - where mode is the mode that created the given - state.

- -

In a nested mode, it is recommended to add an - extra method, innerMode which, given - a state object, returns a {state, mode} object with - the inner mode and its state for the current position. These are - used by utility scripts such as the tag - closer to get context information. Use - the CodeMirror.innerMode helper function to, starting - from a mode and a state, recursively walk down to the innermost - mode and state.

- -

To make indentation work properly in a nested parser, it is - advisable to give the startState method of modes that - are intended to be nested an optional argument that provides the - base indentation for the block of code. The JavaScript and CSS - parser do this, for example, to allow JavaScript and CSS code - inside the mixed-mode HTML mode to be properly indented.

- -

It is possible, and encouraged, to associate - your mode, or a certain configuration of your mode, with - a MIME type. For - example, the JavaScript mode associates itself - with text/javascript, and its JSON variant - with application/json. To do this, - call CodeMirror.defineMIME(mime, - modeSpec), where modeSpec can be a string or - object specifying a mode, as in - the mode option.

- -

If a mode specification wants to add some properties to the - resulting mode object, typically for use - with getHelpers, it may - contain a modeProps property, which holds an object. - This object's properties will be copied to the actual mode - object.

- -

Sometimes, it is useful to add or override mode - object properties from external code. - The CodeMirror.extendMode function - can be used to add properties to mode objects produced for a - specific mode. Its first argument is the name of the mode, its - second an object that specifies the properties that should be - added. This is mostly useful to add utilities that can later be - looked up through getMode.

-
- -
-

VIM Mode API

- -

CodeMirror has a robust VIM mode that attempts to faithfully - emulate VIM's most useful features. It can be enabled by - including keymap/vim.js - and setting the keyMap option to - "vim".

- -

Configuration

- -

VIM mode accepts configuration options for customizing - behavior at run time. These methods can be called at any time - and will affect all existing CodeMirror instances unless - specified otherwise. The methods are exposed on the - CodeMirror.Vim object.

- -
-
setOption(name: string, value: any, ?cm: CodeMirror, ?cfg: object)
-
Sets the value of a VIM option. name should - be the name of an option. If cfg.scope is not set - and cm is provided, then sets the global and - instance values of the option. Otherwise, sets either the - global or instance value of the option depending on whether - cfg.scope is global or - local.
-
getOption(name: string, ?cm: CodeMirror: ?cfg: object)
-
Gets the current value of a VIM option. If - cfg.scope is not set and cm is - provided, then gets the instance value of the option, falling - back to the global value if not set. If cfg.scope is provided, then gets the global or - local value without checking the other.
- -
map(lhs: string, rhs: string, ?context: string)
-
Maps a key sequence to another key sequence. Implements - VIM's :map command. To map ; to : in VIM would be - :map ; :. That would translate to - CodeMirror.Vim.map(';', ':');. - The context can be normal, - visual, or insert, which correspond - to :nmap, :vmap, and - :imap - respectively.
- -
mapCommand(keys: string, type: string, name: string, ?args: object, ?extra: object)
-
Maps a key sequence to a motion, - operator, or action type command. - The args object is passed through to the command when it is - invoked by the provided key sequence. - extras.context can be normal, - visual, or insert, to map the key - sequence only in the corresponding mode. - extras.isEdit is applicable only to actions, - determining whether it is recorded for replay for the - . single-repeat command. - -
unmap(lhs: string, ctx: string)
-
- Remove the command lhs if it is a user defined command. - If the command is an Ex to Ex or Ex to key mapping then the context - must be undefined or false. -
- -
mapclear(ctx: string)
-
- Remove all user-defined mappings for the provided context. -
- -
noremap(lhs: string, rhs: string, ctx: {string, array<string>})
-
- Non-recursive map function. This will not create mappings to key maps - that aren't present in the default key map. - If no context is provided then the mapping will be applied to each of - normal, insert, and visual mode. -
-
- -

Events

- -

VIM mode signals a few events on the editor instance. For an example usage, see demo/vim.html#L101.

- -
-
"vim-command-done" (reason: undefined)
-
Fired on keypress and mousedown where command has completed or no command found.
- -
"vim-keypress" (vimKey: string)
-
Fired on keypress, vimKey is in Vim's key notation.
- -
"vim-mode-change" (modeObj: object)
-
Fired after mode change, modeObj parameter is a {mode: string, ?subMode: string} object. Modes: "insert", "normal", "replace", "visual". Visual sub-modes: "linewise", "blockwise".
-
- -

Extending VIM

- -

CodeMirror's VIM mode implements a large subset of VIM's core - editing functionality. But since there's always more to be - desired, there is a set of APIs for extending VIM's - functionality. As with the configuration API, the methods are - exposed on CodeMirror.Vim and may - be called at any time.

- -
-
defineOption(name: string, default: any, type: string, ?aliases: array<string>, ?callback: function (?value: any, ?cm: CodeMirror) → ?any)
-
Defines a VIM style option and makes it available to the - :set command. Type can be boolean or - string, used for validation and by - :set to determine which syntax to accept. If a - callback is passed in, VIM does not store the value of the - option itself, but instead uses the callback as a setter/getter. If the - first argument to the callback is undefined, then the - callback should return the value of the option. Otherwise, it should set - instead. Since VIM options have global and instance values, whether a - CodeMirror instance is passed in denotes whether the global - or local value should be used. Consequently, it's possible for the - callback to be called twice for a single setOption or - getOption call. Note that right now, VIM does not support - defining buffer-local options that do not have global values. If an - option should not have a global value, either always ignore the - cm parameter in the callback, or always pass in a - cfg.scope to setOption and - getOption.
- -
defineMotion(name: string, fn: function(cm: CodeMirror, head: {line, ch}, ?motionArgs: object}) → {line, ch})
-
Defines a motion command for VIM. The motion should return - the desired result position of the cursor. head - is the current position of the cursor. It can differ from - cm.getCursor('head') if VIM is in visual mode. - motionArgs is the object passed into - mapCommand().
- -
defineOperator(name: string, fn: function(cm: CodeMirror, ?operatorArgs: object, ranges: array<{anchor, head}>) → ?{line, ch})
-
Defines an operator command, similar to - defineMotion. ranges is the range - of text the operator should operate on. If the cursor should - be set to a certain position after the operation finishes, it - can return a cursor object.
- -
defineAction(name: string, fn: function(cm: CodeMirror, ?actionArgs: object))
-
Defines an action command, similar to - defineMotion. Action commands - can have arbitrary behavior, making them more flexible than - motions and operators, at the loss of orthogonality.
- -
defineEx(name: string, ?prefix: string, fn: function(cm: CodeMirror, ?params: object))
-
Defines an Ex command, and maps it to :name. - If a prefix is provided, it, and any prefixed substring of the - name beginning with the prefix can - be used to invoke the command. If the prefix is - falsy, then name is used as the prefix. - params.argString contains the part of the prompted - string after the command name. params.args is - params.argString split by whitespace. If the - command was prefixed with a - line range, - params.line and params.lineEnd will - be set.
- -
getRegisterController()
-
Returns the RegisterController that manages the state of registers - used by vim mode. For the RegisterController api see its - defintion here. -
- -
buildKeyMap()
-
- Not currently implemented. If you would like to contribute this please open - a pull request on Github. -
- -
defineRegister()
-
Defines an external register. The name should be a single character - that will be used to reference the register. The register should support - setText, pushText, clear, and toString. - See Register for a reference implementation. -
- -
getVimGlobalState_()
-
- Return a reference to the VimGlobalState. -
- -
resetVimGlobalState_()
-
- Reset the default values of the VimGlobalState to fresh values. Any options - set with setOption will also be applied to the reset global state. -
- -
maybeInitVimState_(cm: CodeMirror)
-
- Initialize cm.state.vim if it does not exist. Returns cm.state.vim. -
- -
handleKey(cm: CodeMirror, key: string, origin: string)
-
- Convenience function to pass the arguments to findKey and - call returned function if it is defined. -
- -
findKey(cm: CodeMirror, key: string, origin: string)
-
- This is the outermost function called by CodeMirror, after keys have - been mapped to their Vim equivalents. Finds a command based on the key - (and cached keys if there is a multi-key sequence). Returns undefined - if no key is matched, a noop function if a partial match is found (multi-key), - and a function to execute the bound command if a a key is matched. The - function always returns true. -
- -
suppressErrorLogging: boolean
-
Whether to use suppress the use of console.log when catching an - error in the function returned by findKey. - Defaults to false.
- -
exitVisualMode(cm: CodeMirror, ?moveHead: boolean)
-
Exit visual mode. If moveHead is set to false, the CodeMirror selection - will not be touched. The caller assumes the responsibility of putting - the cursor in the right place. -
- -
exitInsertMode(cm: CodeMirror)
-
- Exit insert mode. -
-
- -
- -
- - diff --git a/CODE/js/codemirror/doc/realworld.html b/CODE/js/codemirror/doc/realworld.html deleted file mode 100644 index 5da12e2c..00000000 --- a/CODE/js/codemirror/doc/realworld.html +++ /dev/null @@ -1,205 +0,0 @@ - - -CodeMirror: Real-world Uses - - - - - -
- -

CodeMirror real-world uses

- -

Create a pull - request if you'd like your project to be added to this list.

- - - -
- diff --git a/CODE/js/codemirror/doc/releases.html b/CODE/js/codemirror/doc/releases.html deleted file mode 100644 index 1b4f9a79..00000000 --- a/CODE/js/codemirror/doc/releases.html +++ /dev/null @@ -1,1967 +0,0 @@ - - -CodeMirror: Release History - - - - - - -
- -

Release notes and version history

- -
- -

Version 5.x

- -

19-11-2020: Version 5.58.3:

- -
    -
  • Suppress quick-firing of blur-focus events when dragging and clicking on Internet Explorer.
  • -
  • Fix the insertAt option to addLineWidget to actually allow the widget to be placed after all widgets for the line.
  • -
  • soy mode: Support @Attribute and element composition.
  • -
  • shell mode: Support heredoc quoting.
  • -
- -

23-10-2020: Version 5.58.2:

- -
    -
  • Fix a bug where horizontally scrolling the cursor into view sometimes failed with a non-fixed gutter.
  • -
  • julia mode: Fix an infinite recursion bug.
  • -
- -

21-09-2020: Version 5.58.1:

- - - -

21-09-2020: Version 5.58.0:

- -
    -
  • Make backspace delete by code point, not glyph.
  • -
  • Suppress flickering focus outline when clicking on scrollbars in Chrome.
  • -
  • Fix a bug that prevented attributes added via markText from showing up unless the span also had some other styling.
  • -
  • Suppress cut and paste context menu entries in readonly editors in Chrome.
  • -
  • placeholder addon: Update placeholder visibility during composition.
  • -
  • Make it less cumbersome to style new lint message types.
  • -
  • vim bindings: Support black hole register, gn and gN
  • -
- -

20-08-2020: Version 5.57.0:

- -
    -
  • Fix issue that broke binding the macOS Command key.
  • -
  • comment addon: Keep selection in front of inserted markers when adding a block comment.
  • -
  • css mode: Recognize more properties and value names.
  • -
  • annotatescrollbar addon: Don’t hide matches in collapsed content.
  • -
  • vim bindings: Support tag text objects in xml and html modes.
  • -
- -

20-07-2020: Version 5.56.0:

- -
    -
  • Line-wise pasting was fixed on Chrome Windows.
  • -
  • wast mode: Follow standard changes.
  • -
  • soy mode: Support import expressions, template type, and loop indices.
  • -
  • sql-hint addon: Improve handling of double quotes.
  • -
  • New features

  • -
  • show-hint addon: New option scrollMargin to control how many options are visible beyond the selected one.
  • -
  • hardwrap addon: New option forceBreak to disable breaking of words that are longer than a line.
  • -
- -

21-06-2020: Version 5.55.0:

- -
    -
  • The editor no longer overrides the rendering of zero-width joiners (allowing combined emoji to be shown).
  • -
  • vim bindings: Fix an issue where the vim-mode-change event was fired twice.
  • -
  • javascript mode: Only allow -->-style comments at the start of a line.
  • -
  • julia mode: Improve indentation.
  • -
  • pascal mode: Recognize curly bracket comments.
  • -
  • runmode addon: Further sync up the implementation of the standalone and node variants with the regular library.
  • -
  • New features

  • -
  • loadmode addon: Allow overriding the way the addon constructs filenames and loads modules.
  • -
- -

20-05-2020: Version 5.54.0:

- -
    -
  • runmode addon: Properly support for cross-line lookahead.
  • -
  • vim bindings: Allow Ex-Commands with non-word names.
  • -
  • gfm mode: Add a fencedCodeBlockDefaultMode option.
  • -
  • Improve support for having focus inside in-editor widgets in contenteditable-mode.
  • -
  • Fix issue where the scroll position could jump when clicking on a selection in Chrome.
  • -
  • python mode: Better format string support.
  • -
  • javascript mode: Improve parsing of private properties and class fields.
  • -
  • matchbrackets addon: Disable highlighting when the editor doesn’t have focus.
  • -
- -

21-04-2020: Version 5.53.2:

- - - -

21-04-2020: Version 5.53.0:

- -
    -
  • New option: screenReaderLabel to add a label to the editor.
  • -
  • New mode: wast.
  • -
  • Fix a bug where the editor layout could remain confused after a call to refresh when line wrapping was enabled.
  • -
  • dialog addon: Don’t close dialogs when the document window loses focus.
  • -
  • merge addon: Compensate for editor top position when aligning lines.
  • -
  • vim bindings: Improve EOL handling.
  • -
  • emacs bindings: Include default keymap as a fallback.
  • -
  • julia mode: Fix an infinite loop bug.
  • -
  • show-hint addon: Scroll cursor into view when picking a completion.
  • -
- -

20-03-2020: Version 5.52.2:

- -
    -
  • Fix selection management in contenteditable mode when the editor doesn’t have focus.
  • -
  • Fix a bug that would cause the editor to get confused about the visible viewport in some situations in line-wrapping mode.
  • -
  • markdown mode: Don’t treat single dashes as setext header markers.
  • -
  • zenburn theme: Make sure background styles take precedence over default styles.
  • -
  • css mode: Recognize a number of new properties.
  • -
- -

20-02-2020: Version 5.52.0:

- -
    -
  • Fix a bug in handling of bidi text with Arabic numbers in a right-to-left editor.
  • -
  • Fix a crash when combining file drop with a "beforeChange" filter.
  • -
  • Prevent issue when passing negative coordinates to scrollTo.
  • -
  • lint and tern addons: Allow the tooltip to be appended to the editor wrapper element instead of the document body.
  • -
- -

20-01-2020: Version 5.51.0:

- -
    -
  • Fix the behavior of the home and end keys when direction is set to "rtl".
  • -
  • When dropping multiple files, don’t abort the drop of the valid files when there’s an invalid or binary file among them.
  • -
  • Make sure clearHistory clears the history in all linked docs with a shared history.
  • -
  • vim bindings: Fix behavior of ' and ` marks, fix R in visual mode.
  • -
  • vim bindings: Support gi, gI, and gJ.
  • -
- -

01-01-2020: Version 5.50.2:

- -
    -
  • Fix bug that broke removal of line widgets.
  • -
- -

20-12-2019: Version 5.50.0:

- - - -

21-10-2019: Version 5.49.2:

- - - -

20-09-2019: Version 5.49.0:

- - - -

20-08-2019: Version 5.48.4:

- -
    -
  • Make default styles for line elements more specific so that they don’t apply to all <pre> elements inside the editor.
  • -
  • Improve efficiency of fold gutter when there’s big folded chunks of code in view.
  • -
  • Fix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.
  • -
  • julia mode: Support number separators.
  • -
  • asterisk mode: Improve comment support.
  • -
  • handlebars mode: Support triple-brace tags.
  • -
- -

20-07-2019: Version 5.48.2:

- -
    -
  • vim bindings: Adjust char escape substitution to match vim, support &/$0.
  • -
  • search addon: Try to make backslash behavior in query strings less confusing.
  • -
  • javascript mode: Handle numeric separators, strings in arrow parameter defaults, and TypeScript in operator in index types.
  • -
  • sparql mode: Allow non-ASCII identifier characters.
  • -
- -

20-06-2019: Version 5.48.0:

- -
    -
  • Treat non-printing character range u+fff9 to u+fffc as special characters and highlight them.
  • -
  • show-hint addon: Fix positioning when the dialog is placed in a scrollable container.
  • -
  • Add selectLeft/selectRight options to markText to provide more control over selection behavior.
  • -
- -

21-05-2019: Version 5.47.0:

- -
    -
  • python mode: Properly handle ... syntax.
  • -
  • ruby mode: Fix indenting before closing brackets.
  • -
  • vim bindings: Fix repeat for C-v I, fix handling of fat cursor C-v c Esc and 0, fix @@, fix block-wise yank.
  • -
  • vim bindings: Add support for ` text object.
  • -
- -

22-04-2019: Version 5.46.0:

- -
    -
  • Allow gutters to specify direct CSS stings.
  • -
  • Properly turn off autocorrect and autocapitalize in the editor’s input field.
  • -
  • Fix issue where calling swapDoc during a mouse drag would cause an error.
  • -
  • Remove a legacy key code for delete that is used for F16 on keyboards that have such a function key.
  • -
  • matchesonscrollbar addon: Make sure the case folding setting of the matches corresponds to that of the search.
  • -
  • swift mode: Fix handling of empty strings.
  • -
- -

20-03-2019: Version 5.45.0:

- - - -

21-02-2019: Version 5.44.0:

- -
    -
  • vim bindings: Properly emulate forward-delete.
  • -
  • New theme: nord.
  • -
  • Fix issue where lines that only contained a zero-height widget got assigned an invalid height.
  • -
  • Improve support for middle-click paste on X Windows.
  • -
  • Fix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste.
  • -
  • show-hint addon: Fix accidental global variable.
  • -
  • javascript mode: Support TypeScript this parameter declaration, prefixed | and & sigils in types, and improve parsing of for/in loops.
  • -
- -

21-01-2019: Version 5.43.0:

- -
    -
  • Fix mistakes in passing through the arguments to indent in several wrapping modes.
  • -
  • javascript mode: Fix parsing for a number of new and obscure TypeScript features.
  • -
  • ruby mode: Support indented end tokens for heredoc strings.
  • -
  • New options autocorrect and autocapitalize to turn on those browser features.
  • -
- -

21-12-2018: Version 5.42.2:

- -
    -
  • Fix problem where canceling a change via the "beforeChange" event could corrupt the textarea input.
  • -
  • Fix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE.
  • -
  • vim bindings: Make it possible to select text between angle brackets.
  • -
  • css mode: Fix tokenizing of CSS variables.
  • -
  • python mode: Fix another bug in tokenizing of format strings.
  • -
  • soy mode: More accurate highlighting.
  • -
- -

20-11-2018: Version 5.42.0:

- -
    -
  • The markText method now takes an attributes option that can be used to add attributes text's HTML representation.
  • -
  • vim bindings: Add support for the = binding.
  • -
  • Fix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width.
  • -
  • Optimize handling of window resize events.
  • -
  • show-hint addon: Don't assume the hints are shown in the same document the library was loaded in.
  • -
  • python mode: Fix bug where a string inside a template string broke highlighting.
  • -
  • swift mode: Support multi-line strings.
  • -
- -

25-10-2018: Version 5.41.0:

- -
    -
  • A new selectionsMayTouch option controls whether multiple selections are joined when they touch (the default) or not.
  • -
  • vim bindings: Add noremap binding command.
  • -
  • Fix firing of "gutterContextMenu" event on Firefox.
  • -
  • Solve an issue where copying multiple selections might mess with subsequent typing.
  • -
  • Don't crash when endOperation is called with no operation active.
  • -
  • vim bindings: Fix insert mode repeat after visualBlock edits.
  • -
  • scheme mode: Improve highlighting of quoted expressions.
  • -
  • soy mode: Support injected data and @param in comments.
  • -
  • objective c mode: Improve conformance to the actual language.
  • -
- -

20-09-2018: Version 5.40.2:

- -
    -
  • Fix firing of gutterContextMenu event on Firefox.
  • -
  • Add hintWords (basic completion) helper to clojure, mllike, julia, shell, and r modes.
  • -
  • clojure mode: Clean up and improve.
  • -
- -

25-08-2018: Version 5.40.0:

- -
    -
  • New method phrase and option phrases to make translating UI text in addons easier.
  • -
  • closebrackets addon: Fix issue where bracket-closing wouldn't work before punctuation.
  • -
  • panel addon: Fix problem where replacing the last remaining panel dropped the newly added panel.
  • -
  • hardwrap addon: Fix an infinite loop when the indention is greater than the target column.
  • -
  • jinja2 and markdown modes: Add comment metadata.
  • -
- -

20-07-2018: Version 5.39.2:

- -
    -
  • Fix issue where when you pass the document as a Doc instance to the CodeMirror constructor, the mode option was ignored.
  • -
  • Fix bug where line height could be computed wrong with a line widget below a collapsed line.
  • -
  • Fix overeager .npmignore dropping the bin/source-highlight utility from the distribution.
  • -
  • show-hint addon: Fix behavior when backspacing to the start of the line with completions open.
  • -
- -

20-06-2018: Version 5.39.0:

- -
    -
  • Fix issue that in some circumstances caused content to be clipped off at the bottom after a resize.
  • -
  • markdown mode: Improve handling of blank lines in HTML tags.
  • -
  • stex mode: Add an inMathMode option to start the mode in math mode.
  • -
- -

21-05-2018: Version 5.38.0:

- -
    -
  • Improve reliability of noticing a missing mouseup event during dragging.
  • -
  • Make sure getSelection is always called on the correct document.
  • -
  • Fix interpretation of line breaks and non-breaking spaces inserted by renderer in contentEditable mode.
  • -
  • Work around some browsers inexplicably making the fake scrollbars focusable.
  • -
  • Make sure coordsChar doesn't return positions inside collapsed ranges.
  • -
  • javascript mode: Support block scopes, bindingless catch, bignum suffix, s regexp flag.
  • -
  • markdown mode: Adjust a wasteful regexp.
  • -
  • show-hint addon: Allow opening the control without any item selected.
  • -
  • New theme: darcula.
  • -
  • dialog addon: Add a CSS class (dialog-opened) to the editor when a dialog is open.
  • -
- -

20-04-2018: Version 5.37.0:

- -
    -
  • Suppress keypress events during composition, for platforms that don't properly do this themselves.
  • -
  • xml-fold addon: Improve handling of line-wrapped opening tags.
  • -
  • javascript mode: Improve TypeScript support.
  • -
  • python mode: Highlight expressions inside format strings.
  • -
  • vim bindings: Add support for '(' and ')' movement.
  • -
  • New themes: idea, ssms, gruvbox-dark.
  • -
- -

20-03-2018: Version 5.36.0:

- -
    -
  • Make sure all document-level event handlers are registered on the document that the editor is part of.
  • -
  • Fix issue that prevented edits whose origin starts with + from being combined in history events for an editor-less document.
  • -
  • multiplex addon: Improve handling of indentation.
  • -
  • merge addon: Use CSS :after element to style the scroll-lock icon.
  • -
  • javascript-hint addon: Don't provide completions in JSON mode.
  • -
  • continuelist addon: Fix numbering error.
  • -
  • show-hint addon: Make fromList completion strategy act on the current token up to the cursor, rather than the entire token.
  • -
  • markdown mode: Fix a regexp with potentially exponental complexity.
  • -
  • New theme: lucario.
  • -
- -

20-02-2018: Version 5.35.0:

- -
    -
  • Fix problem where selection undo might change read-only documents.
  • -
  • Fix crash when calling addLineWidget on a document that has no attached editor.
  • -
  • searchcursor addon: Fix behavior of ^ in multiline regexp mode.
  • -
  • match-highlighter addon: Fix problem with matching words that have regexp special syntax in them.
  • -
  • sublime bindings: Fix addCursorToSelection for short lines.
  • -
  • vim bindings: Support alternative delimiters in replace command.
  • -
  • javascript mode: Support TypeScript intersection types, dynamic import.
  • -
  • stex mode: Fix parsing of \( \) delimiters, recognize more atom arguments.
  • -
  • haskell mode: Highlight more builtins, support <* and *>.
  • -
  • sql mode: Make it possible to disable backslash escapes in strings for dialects that don't have them, do this for MS SQL.
  • -
  • dockerfile mode: Highlight strings and ports, recognize more instructions.
  • -
- -

29-01-2018: Version 5.34.0:

- - - -

21-12-2017: Version 5.33.0:

- -
    -
  • lint addon: Make updates more efficient.
  • -
  • css mode: The mode is now properly case-insensitive.
  • -
  • continuelist addon: Fix broken handling of unordered lists introduced in previous release.
  • -
  • swift and scala modes: Support nested block comments.
  • -
  • mllike mode: Improve OCaml support.
  • -
  • sublime bindings: Use the proper key bindings for addCursorToNextLine and addCursorToPrevLine.
  • -
  • jsx mode: Support JSX fragments.
  • -
  • closetag addon: Add an option to disable auto-indenting.
  • -
- -

22-11-2017: Version 5.32.0:

- -
    -
  • Increase contrast on default bracket-matching colors.
  • -
  • javascript mode: Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of enum and module keywords.
  • -
  • comment addon: Fix bug when uncommenting a comment that spans all but the last selected line.
  • -
  • searchcursor addon: Fix bug in case folding.
  • -
  • emacs bindings: Prevent single-character deletions from resetting the kill ring.
  • -
  • closebrackets addon: Tweak quote matching behavior.
  • -
  • continuelist addon: Increment ordered list numbers when adding one.
  • -
- -

20-10-2017: Version 5.31.0:

- -
    -
  • Modes added with addOverlay now have access to a baseToken method on their input stream, giving access to the tokens of the underlying mode.
  • -
  • Further improve selection drawing and cursor motion in right-to-left documents.
  • -
  • vim bindings: Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable input mode.
  • -
  • continuecomment addon: Fix bug when pressing enter after a single-line block comment.
  • -
  • markdown mode: Fix issue with leaving indented fenced code blocks.
  • -
  • javascript mode: Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps.
  • -
- -

20-09-2017: Version 5.30.0:

- -
    -
  • Fixed a number of issues with drawing right-to-left selections and mouse selection in bidirectional text.
  • -
  • search addon: Fix crash when restarting search after doing empty search.
  • -
  • mark-selection addon: Fix off-by-one bug.
  • -
  • tern addon: Fix bad request made when editing at the bottom of a large document.
  • -
  • javascript mode: Improve parsing in a number of corner cases.
  • -
  • markdown mode: Fix crash when a sub-mode doesn't support indentation, allow uppercase X in task lists.
  • -
  • gfm mode: Don't highlight SHA1 'hashes' without numbers to avoid false positives.
  • -
  • soy mode: Support injected data and @param in comments.
  • -
  • simple mode addon: Allow groups in regexps when token isn't an array.
  • -
- -

24-08-2017: Version 5.29.0:

- -
    -
  • Fix crash in contentEditable input style when editing near a bookmark.
  • -
  • Make sure change origins are preserved when splitting changes on read-only marks.
  • -
  • javascript mode: More support for TypeScript syntax.
  • -
  • d mode: Support nested comments.
  • -
  • python mode: Improve tokenizing of operators.
  • -
  • markdown mode: Further improve CommonMark conformance.
  • -
  • css mode: Don't run comment tokens through the mode's state machine.
  • -
  • shell mode: Allow strings to span lines.
  • -
  • search addon: Fix crash in persistent search when extraKeys is null.
  • -
- -

21-07-2017: Version 5.28.0:

- -
    -
  • Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases.
  • -
  • Make "goLineLeft"/"goLineRight" behave better on wrapped lines.
  • -
  • sql mode: Fix tokenizing of multi-dot operator and allow digits in subfield names.
  • -
  • searchcursor addon: Fix infinite loop on some composed character inputs.
  • -
  • markdown mode: Make list parsing more CommonMark-compliant.
  • -
  • gfm mode: Highlight colon syntax for emoji.
  • -
- -

29-06-2017: Version 5.27.4:

- -
    -
  • Fix crash when using mode lookahead.
  • -
  • markdown mode: Don't block inner mode's indentation support.
  • -
- -

22-06-2017: Version 5.27.2:

- - - -

22-06-2017: Version 5.27.0:

- -
    -
  • Fix infinite loop in forced display update.
  • -
  • Properly disable the hidden textarea when readOnly is "nocursor".
  • -
  • Calling the Doc constructor without new works again.
  • -
  • sql mode: Handle nested comments.
  • -
  • javascript mode: Improve support for TypeScript syntax.
  • -
  • markdown mode: Fix bug where markup was ignored on indented paragraph lines.
  • -
  • vim bindings: Referencing invalid registers no longer causes an uncaught exception.
  • -
  • rust mode: Add the correct MIME type.
  • -
  • matchbrackets addon: Document options.
  • -
  • Mouse button clicks can now be bound in keymaps by using names like "LeftClick" or "Ctrl-Alt-MiddleTripleClick". When bound to a function, that function will be passed the position of the click as second argument.
  • -
  • The behavior of mouse selection and dragging can now be customized with the configureMouse option.
  • -
  • Modes can now look ahead across line boundaries with the StringStream.lookahead method.
  • -
  • Introduces a "type" token type, makes modes that recognize types output it, and add styling for it to the themes.
  • -
  • New pasteLinesPerSelection option to control the behavior of pasting multiple lines into multiple selections.
  • -
  • searchcursor addon: Support multi-line regular expression matches, and normalize strings when matching.
  • -
- -

22-05-2017: Version 5.26.0:

- -
    -
  • In textarea-mode, don't reset the input field during composition.
  • -
  • More careful restoration of selections in widgets, during editor redraw.
  • -
  • vim bindings: Parse line offsets in line or range specs.
  • -
  • javascript mode: More TypeScript parsing fixes.
  • -
  • julia mode: Fix issue where the mode gets stuck.
  • -
  • markdown mode: Understand cross-line links, parse all bracketed things as links.
  • -
  • soy mode: Support single-quoted strings.
  • -
  • go mode: Don't try to indent inside strings or comments.
  • -
- -

20-04-2017: Version 5.25.2:

- -
    -
  • Better handling of selections that cover the whole viewport in contentEditable-mode.
  • -
  • No longer accidentally scroll the editor into view when calling setValue.
  • -
  • Work around Chrome Android bug when converting screen coordinates to editor positions.
  • -
  • Make sure long-clicking a selection sets a cursor and doesn't show the editor losing focus.
  • -
  • Fix issue where pointer events were incorrectly disabled on Chrome's overlay scrollbars.
  • -
  • javascript mode: Recognize annotations and TypeScript-style type parameters.
  • -
  • shell mode: Handle nested braces.
  • -
  • markdown mode: Make parsing of strong/em delimiters CommonMark-compliant.
  • -
- -

20-03-2017: Version 5.25.0:

- -
    -
  • In contentEditable-mode, properly locate changes that repeat a character when inserted with IME.
  • -
  • Fix handling of selections bigger than the viewport in contentEditable mode.
  • -
  • Improve handling of changes that insert or delete lines in contentEditable mode.
  • -
  • Count Unicode control characters 0x80 to 0x9F as special (non-printing) chars.
  • -
  • Fix handling of shadow DOM roots when finding the active element.
  • -
  • Add role=presentation to more DOM elements to improve screen reader support.
  • -
  • merge addon: Make aligning of unchanged chunks more robust.
  • -
  • comment addon: Fix comment-toggling on a block of text that starts and ends in a (differnet) block comment.
  • -
  • javascript mode: Improve support for TypeScript syntax.
  • -
  • r mode: Fix indentation after semicolon-less statements.
  • -
  • shell mode: Properly handle escaped parentheses in parenthesized expressions.
  • -
  • markdown mode: Fix a few bugs around leaving fenced code blocks.
  • -
  • soy mode: Improve indentation.
  • -
  • lint addon: Support asynchronous linters that return promises.
  • -
  • continuelist addon: Support continuing task lists.
  • -
  • vim bindings: Make Y behave like yy.
  • -
  • sql mode: Support sqlite dialect.
  • -
- -

22-02-2017: Version 5.24.2:

- -
    -
  • javascript mode: Support computed class method names.
  • -
  • merge addon: Improve aligning of unchanged code in the presence of marks and line widgets.
  • -
- -

20-02-2017: Version 5.24.0:

- -
    -
  • Positions now support a sticky property which determines whether they should be associated with the character before (value "before") or after (value "after") them.
  • -
  • vim bindings: Make it possible to remove built-in bindings through the API.
  • -
  • comment addon: Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings.
  • -
  • A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from.
  • -
  • Visual cursor motion in line-wrapped right-to-left text should be much more correct.
  • -
  • Fix bug in handling of read-only marked text.
  • -
  • shell mode: Properly tokenize nested parentheses.
  • -
  • python mode: Support underscores in number literals.
  • -
  • sass mode: Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. Now depends on the css mode.
  • -
  • css mode: Expose lineComment property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements.
  • -
  • julia mode: Properly indent elseif lines.
  • -
  • markdown mode: Properly recognize the end of fenced code blocks when inside other markup.
  • -
  • scala mode: Improve handling of operators containing #, @, and : chars.
  • -
  • xml mode: Allow dashes in HTML tag names.
  • -
  • javascript mode: Improve parsing of async methods, TypeScript-style comma-separated superclass lists.
  • -
  • indent-fold addon: Ignore comment lines.
  • -
- -

19-01-2017: Version 5.23.0:

- -
    -
  • Presentation-related elements DOM elements are now marked as such to help screen readers.
  • -
  • markdown mode: Be more picky about what HTML tags look like to avoid false positives.
  • -
  • findModeByMIME now understands +json and +xml MIME suffixes.
  • -
  • closebrackets addon: Add support for an override option to ignore language-specific defaults.
  • -
  • panel addon: Add a stable option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.
  • -
- -

20-12-2016: Version 5.22.0:

- -
    -
  • sublime bindings: Make selectBetweenBrackets work with multiple cursors.
  • -
  • javascript mode: Fix issues with parsing complex TypeScript types, imports, and exports.
  • -
  • A contentEditable editor instance with autofocus enabled no longer crashes during initializing.
  • -
  • emacs bindings: Export CodeMirror.emacs to allow other addons to hook into Emacs-style functionality.
  • -
  • active-line addon: Add nonEmpty option.
  • -
  • New event: optionChange.
  • -
- -

21-11-2016: Version 5.21.0:

- -
    -
  • Tapping/clicking the editor in contentEditable mode on Chrome now puts the cursor at the tapped position.
  • -
  • Fix various crashes and misbehaviors when reading composition events in contentEditable mode.
  • -
  • Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a <body>.
  • -
  • merge addon: Fix several issues in the chunk-aligning feature.
  • -
  • verilog mode: Rewritten to address various issues.
  • -
  • julia mode: Recognize Julia 0.5 syntax.
  • -
  • swift mode: Various fixes and adjustments to current syntax.
  • -
  • markdown mode: Allow lists without a blank line above them.
  • -
  • The setGutterMarker, clearGutter, and lineInfo methods are now available on Doc objects.
  • -
  • The heightAtLine method now takes an extra argument to allow finding the height at the top of the line's line widgets.
  • -
  • ruby mode: else and elsif are now immediately indented.
  • -
  • vim bindings: Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode.
  • -
- -

20-10-2016: Version 5.20.0:

- -
    -
  • Make newlineAndIndent command work with multiple cursors on the same line.
  • -
  • Make sure keypress events for backspace are ignored.
  • -
  • Tokens styled with overlays no longer get a nonsense cm-cm-overlay class.
  • -
  • Line endings for pasted content are now normalized to the editor's preferred ending.
  • -
  • javascript mode: Improve support for class expressions. Support TypeScript optional class properties, the abstract keyword, and return type declarations for arrow functions.
  • -
  • css mode: Fix highlighting of mixed-case keywords.
  • -
  • closebrackets addon: Improve behavior when typing a quote before a string.
  • -
  • The core is now maintained as a number of small files, using ES6 syntax and modules, under the src/ directory. A git checkout no longer contains a working codemirror.js until you npm run build (but when installing from NPM, it is included).
  • -
  • The refresh event is now documented and stable.
  • -
- -

20-09-2016: Version 5.19.0:

- -
    -
  • erlang mode: Fix mode crash when trying to read an empty context.
  • -
  • comment addon: Fix broken behavior when toggling comments inside a comment.
  • -
  • xml-fold addon: Fix a null-dereference bug.
  • -
  • Page up and page down now do something even in single-line documents.
  • -
  • Fix an issue where the cursor position could be off in really long (~8000 character) tokens.
  • -
  • javascript mode: Better indentation when semicolons are missing. Better support for TypeScript classes, optional parameters, and the type keyword.
  • -
  • The blur and focus events now pass the DOM event to their handlers.
  • -
- -

23-08-2016: Version 5.18.2:

- -
    -
  • vue mode: Fix outdated references to renamed Pug mode dependency.
  • -
- -

22-08-2016: Version 5.18.0:

- -
    -
  • Make sure gutter backgrounds stick to the rest of the gutter during horizontal scrolling.
  • -
  • The contenteditable inputStyle now properly supports pasting on pre-Edge IE versions.
  • -
  • javascript mode: Fix some small parsing bugs and improve TypeScript support.
  • -
  • matchbrackets addon: Fix bug where active highlighting was left in editor when the addon was disabled.
  • -
  • match-highlighter addon: Only start highlighting things when the editor gains focus.
  • -
  • javascript-hint addon: Also complete non-enumerable properties.
  • -
  • The addOverlay method now supports a priority option to control the order in which overlays are applied.
  • -
  • MIME types that end in +json now default to the JSON mode when the MIME itself is not defined.
  • -
  • The mode formerly known as Jade was renamed to Pug.
  • -
  • The Python mode now defaults to Python 3 (rather than 2) syntax.
  • -
- -

19-07-2016: Version 5.17.0:

- -
    -
  • Fix problem with wrapped trailing whitespace displaying incorrectly.
  • -
  • Prevent IME dialog from overlapping typed content in Chrome.
  • -
  • Improve measuring of characters near a line wrap.
  • -
  • javascript mode: Improve support for async, allow trailing commas in import lists.
  • -
  • vim bindings: Fix backspace in replace mode.
  • -
  • sublime bindings: Fix some key bindings on OS X to match Sublime Text.
  • -
  • markdown mode: Add more classes to image links in highlight-formatting mode.
  • -
- -

20-06-2016: Version 5.16.0:

- -
    -
  • Fix glitches when dragging content caused by the drop indicator receiving mouse events.
  • -
  • Make Control-drag work on Firefox.
  • -
  • Make clicking or selection-dragging at the end of a wrapped line select the right position.
  • -
  • show-hint addon: Prevent widget scrollbar from hiding part of the hint text.
  • -
  • rulers addon: Prevent rulers from forcing a horizontal editor scrollbar.
  • -
  • search addon: Automatically bind search-related keys in persistent dialog.
  • -
  • sublime keymap: Add a multi-cursor aware smart backspace binding.
  • -
- -

20-05-2016: Version 5.15.2:

- -
    -
  • Fix a critical document corruption bug that occurs when a document is gradually grown.
  • -
- -

20-05-2016: Version 5.15.0:

- -
    -
  • Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode.
  • -
  • Fix issue where not all ASCII control characters were being replaced by placeholders.
  • -
  • Remove the assumption that all modes have a startState method from several wrapping modes.
  • -
  • Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any.
  • -
  • Optimize document tree building when loading or pasting huge chunks of content.
  • -
  • Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected.
  • -
  • Pasting linewise-copied content when there is no selection now inserts the lines above the current line.
  • -
  • markdown mode: Fix several issues in matching link targets.
  • -
  • clike mode: Improve indentation of C++ template declarations.
  • -
  • javascript mode: Support async/await and improve support for TypeScript type syntax.
  • -
- -

20-04-2016: Version 5.14.0:

- - - -

21-03-2016: Version 5.13.2:

- -
    -
  • Solves a problem where the gutter would sometimes not extend all the way to the end of the document.
  • -
- -

21-03-2016: Version 5.13:

- - - -

19-02-2016: Version 5.12:

- -
    -
  • Vim bindings: Ctrl-Q is now an alias for Ctrl-V.
  • -
  • Vim bindings: The Vim API now exposes an unmap method to unmap bindings.
  • -
  • active-line addon: This addon can now style the active line's gutter.
  • -
  • FCL mode: Newly added.
  • -
  • SQL mode: Now has a Postgresql dialect.
  • -
  • Fix issue where trying to scroll to a horizontal position outside of the document's width could cause the gutter to be positioned incorrectly.
  • -
  • Use absolute, rather than fixed positioning in the context-menu intercept hack, to work around a problem when the editor is inside a transformed parent container.
  • -
  • Solve a problem where the horizontal scrollbar could hide text in Firefox.
  • -
  • Fix a bug that caused phantom scroll space under the text in some situations.
  • -
  • Sublime Text bindings: Bind delete-line to Shift-Ctrl-K on OS X.
  • -
  • Markdown mode: Fix issue where the mode would keep state related to fenced code blocks in an unsafe way, leading to occasional corrupted parses.
  • -
  • Markdown mode: Ignore backslashes in code fragments.
  • -
  • Markdown mode: Use whichever mode is registered as text/html to parse HTML.
  • -
  • Clike mode: Improve indentation of Scala => functions.
  • -
  • Python mode: Improve indentation of bracketed code.
  • -
  • HTMLMixed mode: Support multi-line opening tags for sub-languages (<script>, <style>, etc).
  • -
  • Spreadsheet mode: Fix bug where the mode did not advance the stream when finding a backslash.
  • -
  • XML mode: The mode now takes a matchClosing option to configure whether mismatched closing tags should be highlighted as errors.
  • -
- -

20-01-2016: Version 5.11:

- -
    -
  • New modes: JSX, literate Haskell
  • -
  • The editor now forwards more DOM events: cut, copy, paste, and touchstart. It will also forward mousedown for drag events
  • -
  • Fixes a bug where bookmarks next to collapsed spans were not rendered
  • -
  • The Swift mode now supports auto-indentation
  • -
  • Frontmatters in the YAML frontmatter mode are now optional as intended
  • -
  • Full list of patches
  • -
- -

21-12-2015: Version 5.10:

- - - -

23-11-2015: Version 5.9:

- -
    -
  • Improve the way overlay (OS X-style) scrollbars are handled
  • -
  • Make annotatescrollbar and scrollpastend addons work properly together
  • -
  • Make show-hint addon select options on single click by default, move selection to hovered item
  • -
  • Properly fold comments that include block-comment-start markers
  • -
  • Many small language mode fixes
  • -
  • Full list of patches
  • -
- -

20-10-2015: Version 5.8:

- - - -

20-09-2015: Version 5.7:

- - - -

20-08-2015: Version 5.6:

- -
    -
  • Fix bug where you could paste into a readOnly editor
  • -
  • Show a cursor at the drop location when dragging over the editor
  • -
  • The Rust mode was rewritten to handle modern Rust
  • -
  • The editor and theme CSS was cleaned up. Some selectors are now less specific than before
  • -
  • New theme: abcdef
  • -
  • Lines longer than maxHighlightLength are now less likely to mess up indentation
  • -
  • New addons: autorefresh for refreshing an editor the first time it becomes visible, and html-lint for using HTMLHint
  • -
  • The search addon now recognizes \r and \n in pattern and replacement input
  • -
  • Full list of patches
  • -
- -

20-07-2015: Version 5.5:

- - - -

25-06-2015: Version 5.4:

- - - -

20-05-2015: Version 5.3:

- - - -

20-04-2015: Version 5.2:

- - - -

23-03-2015: Version 5.1:

- - - -

20-02-2015: Version 5.0:

- -
    -
  • Experimental mobile support (tested on iOS, Android Chrome, stock Android browser)
  • -
  • New option inputStyle to switch between hidden textarea and contenteditable input.
  • -
  • The getInputField - method is no longer guaranteed to return a textarea.
  • -
  • Full list of patches.
  • -
- -
- -
- -

Version 4.x

- -

20-02-2015: Version 4.13:

- - - -

22-01-2015: Version 4.12:

- - - -

9-01-2015: Version 4.11:

- -

Unfortunately, 4.10 did not take care of the - Firefox scrolling issue entirely. This release adds two more patches - to address that.

- -

29-12-2014: Version 4.10:

- -

Emergency single-patch update to 4.9. Fixes - Firefox-specific problem where the cursor could end up behind the - horizontal scrollbar.

- -

23-12-2014: Version 4.9:

- - - -

22-11-2014: Version 4.8:

- - - -

20-10-2014: Version 4.7:

- -
    -
  • Incompatible: - The lint addon now passes the - editor's value as first argument to asynchronous lint functions, - for consistency. The editor is still passed, as fourth - argument.
  • -
  • Improved handling of unicode identifiers in modes for - languages that support them.
  • -
  • More mode - improvements: CoffeeScript - (indentation), Verilog - (indentation), Scala - (indentation, triple-quoted strings), - and PHP (interpolated - variables in heredoc strings).
  • -
  • New modes: Textile and Tornado templates.
  • -
  • Experimental new way to define modes.
  • -
  • Improvements to the Vim - bindings: Arbitrary insert mode key mappings are now possible, - and text objects are supported in visual mode.
  • -
  • The mode meta-information file - now includes information about file extensions, - and helper - functions findModeByMIME - and findModeByExtension.
  • -
  • New logo!
  • -
  • Full list of patches.
  • -
- -

19-09-2014: Version 4.6:

- - - -

21-08-2014: Version 4.5:

- -
    -
  • Fix several serious bugs with horizontal scrolling
  • -
  • New mode: Slim
  • -
  • New command: goLineLeftSmart
  • -
  • More fixes and extensions for the Vim visual block mode
  • -
  • Full list of patches.
  • -
- -

21-07-2014: Version 4.4:

- -
    -
  • Note: Some events might now fire in slightly - different order ("change" is still guaranteed to fire - before "cursorActivity")
  • -
  • Nested operations in multiple editors are now synced (complete - at same time, reducing DOM reflows)
  • -
  • Visual block mode for vim (<C-v>) is nearly complete
  • -
  • New mode: Kotlin
  • -
  • Better multi-selection paste for text copied from multiple CodeMirror selections
  • -
  • Full list of patches.
  • -
- -

23-06-2014: Version 4.3:

- -
    -
  • Several vim bindings - improvements: search and exCommand history, global flag - for :substitute, :global command. -
  • Allow hiding the cursor by - setting cursorBlinkRate - to a negative value.
  • -
  • Make gutter markers themeable, use this in foldgutter.
  • -
  • Full list of patches.
  • -
- -

19-05-2014: Version 4.2:

- -
    -
  • Fix problem where some modes were broken by the fact that empty tokens were forbidden.
  • -
  • Several fixes to context menu handling.
  • -
  • On undo, scroll change, not cursor, into view.
  • -
  • Rewritten Jade mode.
  • -
  • Various improvements to Shell (support for more syntax) and Python (better indentation) modes.
  • -
  • New mode: Cypher.
  • -
  • New theme: Neo.
  • -
  • Support direct styling options (color, line style, width) in the rulers addon.
  • -
  • Recognize per-editor configuration for the show-hint and foldcode addons.
  • -
  • More intelligent scanning for existing close tags in closetag addon.
  • -
  • In the Vim bindings: Fix bracket matching, support case conversion in visual mode, visual paste, append action.
  • -
  • Full list of patches.
  • -
- -

22-04-2014: Version 4.1:

- -
    -
  • Slightly incompatible: - The "cursorActivity" - event now fires after all other events for the operation (and only - for handlers that were actually registered at the time the - activity happened).
  • -
  • New command: insertSoftTab.
  • -
  • New mode: Django.
  • -
  • Improved modes: Verilog (rewritten), Jinja2, Haxe, PHP (string interpolation highlighted), JavaScript (indentation of trailing else, template strings), LiveScript (multi-line strings).
  • -
  • Many small issues from the 3.x→4.x transition were found and fixed.
  • -
  • Full list of patches.
  • -
- -

20-03-2014: Version 4.0:

- -

This is a new major version of CodeMirror. There - are a few incompatible changes in the API. Upgrade - with care, and read the upgrading - guide.

- - - -
- -
- -

Version 3.x

- -

22-04-2014: Version 3.24:

- -

Merges the improvements from 4.1 that could - easily be applied to the 3.x code. Also improves the way the editor - size is updated when line widgets change.

- -

20-03-2014: Version 3.23:

- -
    -
  • In the XML mode, - add brackets style to angle brackets, fix - case-sensitivity of tags for HTML.
  • -
  • New mode: Dylan.
  • -
  • Many improvements to the Vim bindings.
  • -
- -

21-02-2014: Version 3.22:

- - - -

16-01-2014: Version 3.21:

- -
    -
  • Auto-indenting a block will no longer add trailing whitespace to blank lines.
  • -
  • Marking text has a new option clearWhenEmpty to control auto-removal.
  • -
  • Several bugfixes in the handling of bidirectional text.
  • -
  • The XML and CSS modes were largely rewritten. LESS support was added to the CSS mode.
  • -
  • The OCaml mode was moved to an mllike mode, F# support added.
  • -
  • Make it possible to fetch multiple applicable helper values with getHelpers, and to register helpers matched on predicates with registerGlobalHelper.
  • -
  • New theme pastel-on-dark.
  • -
  • Better ECMAScript 6 support in JavaScript mode.
  • -
  • Full list of patches.
  • -
- -

21-11-2013: Version 3.20:

- - - -

21-10-2013: Version 3.19:

- - - -

23-09-2013: Version 3.18:

- -

Emergency release to fix a problem in 3.17 - where .setOption("lineNumbers", false) would raise an - error.

- -

23-09-2013: Version 3.17:

- - - -

21-08-2013: Version 3.16:

- - - -

29-07-2013: Version 3.15:

- - - -

20-06-2013: Version 3.14:

- - - -

20-05-2013: Version 3.13:

- - - -

19-04-2013: Version 3.12:

- - - -

20-03-2013: Version 3.11:

- - - -

21-02-2013: Version 3.1:

- - - - -

25-01-2013: Version 3.02:

- -

Single-bugfix release. Fixes a problem that - prevents CodeMirror instances from being garbage-collected after - they become unused.

- -

21-01-2013: Version 3.01:

- - - -

10-12-2012: Version 3.0:

- -

New major version. Only - partially backwards-compatible. See - the upgrading guide for more - information. Changes since release candidate 2:

- -
    -
  • Rewritten VIM mode.
  • -
  • Fix a few minor scrolling and sizing issues.
  • -
  • Work around Safari segfault when dragging.
  • -
  • Full list of patches.
  • -
- -

20-11-2012: Version 3.0, release candidate 2:

- -
    -
  • New mode: HTTP.
  • -
  • Improved handling of selection anchor position.
  • -
  • Improve IE performance on longer lines.
  • -
  • Reduce gutter glitches during horiz. scrolling.
  • -
  • Add addKeyMap and removeKeyMap methods.
  • -
  • Rewrite formatting and closetag add-ons.
  • -
  • Full list of patches.
  • -
- -

20-11-2012: Version 3.0, release candidate 1:

- - - -

22-10-2012: Version 3.0, beta 2:

- -
    -
  • Fix page-based coordinate computation.
  • -
  • Fix firing of gutterClick event.
  • -
  • Add cursorHeight option.
  • -
  • Fix bi-directional text regression.
  • -
  • Add viewportMargin option.
  • -
  • Directly handle mousewheel events (again, hopefully better).
  • -
  • Make vertical cursor movement more robust (through widgets, big line gaps).
  • -
  • Add flattenSpans option.
  • -
  • Many optimizations. Poor responsiveness should be fixed.
  • -
  • Initialization in hidden state works again.
  • -
  • Full list of patches.
  • -
- -

19-09-2012: Version 3.0, beta 1:

- -
    -
  • Bi-directional text support.
  • -
  • More powerful gutter model.
  • -
  • Support for arbitrary text/widget height.
  • -
  • In-line widgets.
  • -
  • Generalized event handling.
  • -
- -
- -
- -

Version 2.x

- -

21-01-2013: Version 2.38:

- -

Integrate some bugfixes, enhancements to the vim keymap, and new - modes - (D, Sass, APL) - from the v3 branch.

- -

20-12-2012: Version 2.37:

- -
    -
  • New mode: SQL (will replace plsql and mysql modes).
  • -
  • Further work on the new VIM mode.
  • -
  • Fix Cmd/Ctrl keys on recent Operas on OS X.
  • -
  • Full list of patches.
  • -
- -

20-11-2012: Version 2.36:

- - - -

22-10-2012: Version 2.35:

- -
    -
  • New (sub) mode: TypeScript.
  • -
  • Don't overwrite (insert key) when pasting.
  • -
  • Fix several bugs in markText/undo interaction.
  • -
  • Better indentation of JavaScript code without semicolons.
  • -
  • Add defineInitHook function.
  • -
  • Full list of patches.
  • -
- -

19-09-2012: Version 2.34:

- -
    -
  • New mode: Common Lisp.
  • -
  • Fix right-click select-all on most browsers.
  • -
  • Change the way highlighting happens:
      Saves memory and CPU cycles.
      compareStates is no longer needed.
      onHighlightComplete no longer works.
  • -
  • Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite.
  • -
  • Add a CodeMirror.version property.
  • -
  • More robust handling of nested modes in formatting and closetag plug-ins.
  • -
  • Un/redo now preserves marked text and bookmarks.
  • -
  • Full list of patches.
  • -
- -

23-08-2012: Version 2.33:

- -
    -
  • New mode: Sieve.
  • -
  • New getViewPort and onViewportChange API.
  • -
  • Configurable cursor blink rate.
  • -
  • Make binding a key to false disabling handling (again).
  • -
  • Show non-printing characters as red dots.
  • -
  • More tweaks to the scrolling model.
  • -
  • Expanded testsuite. Basic linter added.
  • -
  • Remove most uses of innerHTML. Remove CodeMirror.htmlEscape.
  • -
  • Full list of patches.
  • -
- -

23-07-2012: Version 2.32:

- -

Emergency fix for a bug where an editor with - line wrapping on IE will break when there is no - scrollbar.

- -

20-07-2012: Version 2.31:

- - - -

22-06-2012: Version 2.3:

- -
    -
  • New scrollbar implementation. Should flicker less. Changes DOM structure of the editor.
  • -
  • New theme: vibrant-ink.
  • -
  • Many extensions to the VIM keymap (including text objects).
  • -
  • Add mode-multiplexing utility script.
  • -
  • Fix bug where right-click paste works in read-only mode.
  • -
  • Add a getScrollInfo method.
  • -
  • Lots of other fixes.
  • -
- -

23-05-2012: Version 2.25:

- -
    -
  • New mode: Erlang.
  • -
  • Remove xmlpure mode (use xml.js).
  • -
  • Fix line-wrapping in Opera.
  • -
  • Fix X Windows middle-click paste in Chrome.
  • -
  • Fix bug that broke pasting of huge documents.
  • -
  • Fix backspace and tab key repeat in Opera.
  • -
- -

23-04-2012: Version 2.24:

- -
    -
  • Drop support for Internet Explorer 6.
  • -
  • New - modes: Shell, Tiki - wiki, Pig Latin.
  • -
  • New themes: Ambiance, Blackboard.
  • -
  • More control over drag/drop - with dragDrop - and onDragEvent - options.
  • -
  • Make HTML mode a bit less pedantic.
  • -
  • Add compoundChange API method.
  • -
  • Several fixes in undo history and line hiding.
  • -
  • Remove (broken) support for catchall in key maps, - add nofallthrough boolean field instead.
  • -
- -

26-03-2012: Version 2.23:

- -
    -
  • Change default binding for tab [more] - -
  • -
  • New modes: XQuery and VBScript.
  • -
  • Two new themes: lesser-dark and xq-dark.
  • -
  • Differentiate between background and text styles in setLineClass.
  • -
  • Fix drag-and-drop in IE9+.
  • -
  • Extend charCoords - and cursorCoords with a mode argument.
  • -
  • Add autofocus option.
  • -
  • Add findMarksAt method.
  • -
- -

27-02-2012: Version 2.22:

- - - -

27-01-2012: Version 2.21:

- -
    -
  • Added LESS, MySQL, - Go, and Verilog modes.
  • -
  • Add smartIndent - option.
  • -
  • Support a cursor in readOnly-mode.
  • -
  • Support assigning multiple styles to a token.
  • -
  • Use a new approach to drawing the selection.
  • -
  • Add scrollTo method.
  • -
  • Allow undo/redo events to span non-adjacent lines.
  • -
  • Lots and lots of bugfixes.
  • -
- -

20-12-2011: Version 2.2:

- - - -

21-11-2011: Version 2.18:

-

Fixes TextMarker.clear, which is broken in 2.17.

- -

21-11-2011: Version 2.17:

-
    -
  • Add support for line - wrapping and code - folding.
  • -
  • Add Github-style Markdown mode.
  • -
  • Add Monokai - and Rubyblue themes.
  • -
  • Add setBookmark method.
  • -
  • Move some of the demo code into reusable components - under lib/util.
  • -
  • Make screen-coord-finding code faster and more reliable.
  • -
  • Fix drag-and-drop in Firefox.
  • -
  • Improve support for IME.
  • -
  • Speed up content rendering.
  • -
  • Fix browser's built-in search in Webkit.
  • -
  • Make double- and triple-click work in IE.
  • -
  • Various fixes to modes.
  • -
- -

27-10-2011: Version 2.16:

-
    -
  • Add Perl, Rust, TiddlyWiki, and Groovy modes.
  • -
  • Dragging text inside the editor now moves, rather than copies.
  • -
  • Add a coordsFromIndex method.
  • -
  • API change: setValue now no longer clears history. Use clearHistory for that.
  • -
  • API change: markText now - returns an object with clear and find - methods. Marked text is now more robust when edited.
  • -
  • Fix editing code with tabs in Internet Explorer.
  • -
- -

26-09-2011: Version 2.15:

-

Fix bug that snuck into 2.14: Clicking the - character that currently has the cursor didn't re-focus the - editor.

- -

26-09-2011: Version 2.14:

- - - -

23-08-2011: Version 2.13:

- - -

25-07-2011: Version 2.12:

-
    -
  • Add a SPARQL mode.
  • -
  • Fix bug with cursor jumping around in an unfocused editor in IE.
  • -
  • Allow key and mouse events to bubble out of the editor. Ignore widget clicks.
  • -
  • Solve cursor flakiness after undo/redo.
  • -
  • Fix block-reindent ignoring the last few lines.
  • -
  • Fix parsing of multi-line attrs in XML mode.
  • -
  • Use innerHTML for HTML-escaping.
  • -
  • Some fixes to indentation in C-like mode.
  • -
  • Shrink horiz scrollbars when long lines removed.
  • -
  • Fix width feedback loop bug that caused the width of an inner DIV to shrink.
  • -
- -

04-07-2011: Version 2.11:

-
    -
  • Add a Scheme mode.
  • -
  • Add a replace method to search cursors, for cursor-preserving replacements.
  • -
  • Make the C-like mode mode more customizable.
  • -
  • Update XML mode to spot mismatched tags.
  • -
  • Add getStateAfter API and compareState mode API methods for finer-grained mode magic.
  • -
  • Add a getScrollerElement API method to manipulate the scrolling DIV.
  • -
  • Fix drag-and-drop for Firefox.
  • -
  • Add a C# configuration for the C-like mode.
  • -
  • Add full-screen editing and mode-changing demos.
  • -
- -

07-06-2011: Version 2.1:

-

Add - a theme system - (demo). Note that this is not - backwards-compatible—you'll have to update your styles and - modes!

- -

07-06-2011: Version 2.02:

-
    -
  • Add a Lua mode.
  • -
  • Fix reverse-searching for a regexp.
  • -
  • Empty lines can no longer break highlighting.
  • -
  • Rework scrolling model (the outer wrapper no longer does the scrolling).
  • -
  • Solve horizontal jittering on long lines.
  • -
  • Add runmode.js.
  • -
  • Immediately re-highlight text when typing.
  • -
  • Fix problem with 'sticking' horizontal scrollbar.
  • -
- -

26-05-2011: Version 2.01:

-
    -
  • Add a Smalltalk mode.
  • -
  • Add a reStructuredText mode.
  • -
  • Add a Python mode.
  • -
  • Add a PL/SQL mode.
  • -
  • coordsChar now works
  • -
  • Fix a problem where onCursorActivity interfered with onChange.
  • -
  • Fix a number of scrolling and mouse-click-position glitches.
  • -
  • Pass information about the changed lines to onChange.
  • -
  • Support cmd-up/down on OS X.
  • -
  • Add triple-click line selection.
  • -
  • Don't handle shift when changing the selection through the API.
  • -
  • Support "nocursor" mode for readOnly option.
  • -
  • Add an onHighlightComplete option.
  • -
  • Fix the context menu for Firefox.
  • -
- -

28-03-2011: Version 2.0:

-

CodeMirror 2 is a complete rewrite that's - faster, smaller, simpler to use, and less dependent on browser - quirks. See this - and this - for more information.

- -

22-02-2011: Version 2.0 beta 2:

-

Somewhat more mature API, lots of bugs shaken out.

- -

17-02-2011: Version 0.94:

-
    -
  • tabMode: "spaces" was modified slightly (now indents when something is selected).
  • -
  • Fixes a bug that would cause the selection code to break on some IE versions.
  • -
  • Disabling spell-check on WebKit browsers now works.
  • -
- -

08-02-2011: Version 2.0 beta 1:

-

CodeMirror 2 is a complete rewrite of - CodeMirror, no longer depending on an editable frame.

- -

19-01-2011: Version 0.93:

-
    -
  • Added a Regular Expression parser.
  • -
  • Fixes to the PHP parser.
  • -
  • Support for regular expression in search/replace.
  • -
  • Add save method to instances created with fromTextArea.
  • -
  • Add support for MS T-SQL in the SQL parser.
  • -
  • Support use of CSS classes for highlighting brackets.
  • -
  • Fix yet another hang with line-numbering in hidden editors.
  • -
-
- -
- -

Version 0.x

- -

28-03-2011: Version 1.0:

-
    -
  • Fix error when debug history overflows.
  • -
  • Refine handling of C# verbatim strings.
  • -
  • Fix some issues with JavaScript indentation.
  • -
- -

17-12-2010: Version 0.92:

-
    -
  • Make CodeMirror work in XHTML documents.
  • -
  • Fix bug in handling of backslashes in Python strings.
  • -
  • The styleNumbers option is now officially - supported and documented.
  • -
  • onLineNumberClick option added.
  • -
  • More consistent names onLoad and - onCursorActivity callbacks. Old names still work, but - are deprecated.
  • -
  • Add a Freemarker mode.
  • -
- -

11-11-2010: Version 0.91:

-
    -
  • Adds support for Java.
  • -
  • Small additions to the PHP and SQL parsers.
  • -
  • Work around various Webkit issues.
  • -
  • Fix toTextArea to update the code in the textarea.
  • -
  • Add a noScriptCaching option (hack to ease development).
  • -
  • Make sub-modes of HTML mixed mode configurable.
  • -
- -

02-10-2010: Version 0.9:

-
    -
  • Add support for searching backwards.
  • -
  • There are now parsers for Scheme, XQuery, and OmetaJS.
  • -
  • Makes height: "dynamic" more robust.
  • -
  • Fixes bug where paste did not work on OS X.
  • -
  • Add a enterMode and electricChars options to make indentation even more customizable.
  • -
  • Add firstLineNumber option.
  • -
  • Fix bad handling of @media rules by the CSS parser.
  • -
  • Take a new, more robust approach to working around the invisible-last-line bug in WebKit.
  • -
- -

22-07-2010: Version 0.8:

-
    -
  • Add a cursorCoords method to find the screen - coordinates of the cursor.
  • -
  • A number of fixes and support for more syntax in the PHP parser.
  • -
  • Fix indentation problem with JSON-mode JS parser in Webkit.
  • -
  • Add a minification UI.
  • -
  • Support a height: dynamic mode, where the editor's - height will adjust to the size of its content.
  • -
  • Better support for IME input mode.
  • -
  • Fix JavaScript parser getting confused when seeing a no-argument - function call.
  • -
  • Have CSS parser see the difference between selectors and other - identifiers.
  • -
  • Fix scrolling bug when pasting in a horizontally-scrolled - editor.
  • -
  • Support toTextArea method in instances created with - fromTextArea.
  • -
  • Work around new Opera cursor bug that causes the cursor to jump - when pressing backspace at the end of a line.
  • -
- -

27-04-2010: Version - 0.67:

-

More consistent page-up/page-down behaviour - across browsers. Fix some issues with hidden editors looping forever - when line-numbers were enabled. Make PHP parser parse - "\\" correctly. Have jumpToLine work on - line handles, and add cursorLine function to fetch the - line handle where the cursor currently is. Add new - setStylesheet function to switch style-sheets in a - running editor.

- -

01-03-2010: Version - 0.66:

-

Adds removeLine method to API. - Introduces the PLSQL parser. - Marks XML errors by adding (rather than replacing) a CSS class, so - that they can be disabled by modifying their style. Fixes several - selection bugs, and a number of small glitches.

- -

12-11-2009: Version - 0.65:

-

Add support for having both line-wrapping and - line-numbers turned on, make paren-highlighting style customisable - (markParen and unmarkParen config - options), work around a selection bug that Opera - reintroduced in version 10.

- -

23-10-2009: Version - 0.64:

-

Solves some issues introduced by the - paste-handling changes from the previous release. Adds - setSpellcheck, setTextWrapping, - setIndentUnit, setUndoDepth, - setTabMode, and setLineNumbers to - customise a running editor. Introduces an SQL parser. Fixes a few small - problems in the Python - parser. And, as usual, add workarounds for various newly discovered - browser incompatibilities.

- -

31-08-2009: Version 0.63:

-

Overhaul of paste-handling (less fragile), fixes for several - serious IE8 issues (cursor jumping, end-of-document bugs) and a number - of small problems.

- -

30-05-2009: Version 0.62:

-

Introduces Python - and Lua parsers. Add - setParser (on-the-fly mode changing) and - clearHistory methods. Make parsing passes time-based - instead of lines-based (see the passTime option).

- -
-
diff --git a/CODE/js/codemirror/doc/reporting.html b/CODE/js/codemirror/doc/reporting.html deleted file mode 100644 index 32d7986d..00000000 --- a/CODE/js/codemirror/doc/reporting.html +++ /dev/null @@ -1,60 +0,0 @@ - - -CodeMirror: Reporting Bugs - - - - - -
- -

Reporting bugs effectively

- -
- -

So you found a problem in CodeMirror. By all means, report it! Bug -reports from users are the main drive behind improvements to -CodeMirror. But first, please read over these points:

- -
    -
  1. CodeMirror is maintained by volunteers. They don't owe you - anything, so be polite. Reports with an indignant or belligerent - tone tend to be moved to the bottom of the pile.
  2. - -
  3. Include information about the browser in which the - problem occurred. Even if you tested several browsers, and - the problem occurred in all of them, mention this fact in the bug - report. Also include browser version numbers and the operating - system that you're on.
  4. - -
  5. Mention which release of CodeMirror you're using. Preferably, - try also with the current development snapshot, to ensure the - problem has not already been fixed.
  6. - -
  7. Mention very precisely what went wrong. "X is broken" is not a - good bug report. What did you expect to happen? What happened - instead? Describe the exact steps a maintainer has to take to reproduce - the error. We can not fix something that we can not observe.
  8. - -
  9. If the problem can not be reproduced in any of the demos - included in the CodeMirror distribution, please provide an HTML - document that demonstrates the problem. The best way to do this is - to go to jsbin.com, enter - it there, press save, and include the resulting link in your bug - report.
  10. -
- -
- -
diff --git a/CODE/js/codemirror/doc/upgrade_v2.2.html b/CODE/js/codemirror/doc/upgrade_v2.2.html deleted file mode 100644 index 5709e652..00000000 --- a/CODE/js/codemirror/doc/upgrade_v2.2.html +++ /dev/null @@ -1,96 +0,0 @@ - - -CodeMirror: Version 2.2 upgrade guide - - - - - -
- -

Upgrading to v2.2

- -

There are a few things in the 2.2 release that require some care -when upgrading.

- -

No more default.css

- -

The default theme is now included -in codemirror.css, so -you do not have to included it separately anymore. (It was tiny, so -even if you're not using it, the extra data overhead is negligible.) - -

Different key customization

- -

CodeMirror has moved to a system -where keymaps are used to -bind behavior to keys. This means custom -bindings are now possible.

- -

Three options that influenced key -behavior, tabMode, enterMode, -and smartHome, are no longer supported. Instead, you can -provide custom bindings to influence the way these keys act. This is -done through the -new extraKeys -option, which can hold an object mapping key names to functionality. A -simple example would be:

- -
  extraKeys: {
-    "Ctrl-S": function(instance) { saveText(instance.getValue()); },
-    "Ctrl-/": "undo"
-  }
- -

Keys can be mapped either to functions, which will be given the -editor instance as argument, or to strings, which are mapped through -functions through the CodeMirror.commands table, which -contains all the built-in editing commands, and can be inspected and -extended by external code.

- -

By default, the Home key is bound to -the "goLineStartSmart" command, which moves the cursor to -the first non-whitespace character on the line. You can set do this to -make it always go to the very start instead:

- -
  extraKeys: {"Home": "goLineStart"}
- -

Similarly, Enter is bound -to "newlineAndIndent" by default. You can bind it to -something else to get different behavior. To disable special handling -completely and only get a newline character inserted, you can bind it -to false:

- -
  extraKeys: {"Enter": false}
- -

The same works for Tab. If you don't want CodeMirror -to handle it, bind it to false. The default behaviour is -to indent the current line more ("indentMore" command), -and indent it less when shift is held ("indentLess"). -There are also "indentAuto" (smart indent) -and "insertTab" commands provided for alternate -behaviors. Or you can write your own handler function to do something -different altogether.

- -

Tabs

- -

Handling of tabs changed completely. The display width of tabs can -now be set with the tabSize option, and tabs can -be styled by setting CSS rules -for the cm-tab class.

- -

The default width for tabs is now 4, as opposed to the 8 that is -hard-wired into browsers. If you are relying on 8-space tabs, make -sure you explicitly set tabSize: 8 in your options.

- -
diff --git a/CODE/js/codemirror/doc/upgrade_v3.html b/CODE/js/codemirror/doc/upgrade_v3.html deleted file mode 100644 index 2fec440f..00000000 --- a/CODE/js/codemirror/doc/upgrade_v3.html +++ /dev/null @@ -1,230 +0,0 @@ - - -CodeMirror: Version 3 upgrade guide - - - - - - - - - - - - - - -
- -

Upgrading to version 3

- -

Version 3 does not depart too much from 2.x API, and sites that use -CodeMirror in a very simple way might be able to upgrade without -trouble. But it does introduce a number of incompatibilities. Please -at least skim this text before upgrading.

- -

Note that version 3 drops full support for Internet -Explorer 7. The editor will mostly work on that browser, but -it'll be significantly glitchy.

- -
-

DOM structure

- -

This one is the most likely to cause problems. The internal -structure of the editor has changed quite a lot, mostly to implement a -new scrolling model.

- -

Editor height is now set on the outer wrapper element (CSS -class CodeMirror), not on the scroller element -(CodeMirror-scroll).

- -

Other nodes were moved, dropped, and added. If you have any code -that makes assumptions about the internal DOM structure of the editor, -you'll have to re-test it and probably update it to work with v3.

- -

See the styling section of the -manual for more information.

-
-
-

Gutter model

- -

In CodeMirror 2.x, there was a single gutter, and line markers -created with setMarker would have to somehow coexist with -the line numbers (if present). Version 3 allows you to specify an -array of gutters, by class -name, -use setGutterMarker -to add or remove markers in individual gutters, and clear whole -gutters -with clearGutter. -Gutter markers are now specified as DOM nodes, rather than HTML -snippets.

- -

The gutters no longer horizontally scrolls along with the content. -The fixedGutter option was removed (since it is now the -only behavior).

- -
-<style>
-  /* Define a gutter style */
-  .note-gutter { width: 3em; background: cyan; }
-</style>
-<script>
-  // Create an instance with two gutters -- line numbers and notes
-  var cm = new CodeMirror(document.body, {
-    gutters: ["note-gutter", "CodeMirror-linenumbers"],
-    lineNumbers: true
-  });
-  // Add a note to line 0
-  cm.setGutterMarker(0, "note-gutter", document.createTextNode("hi"));
-</script>
-
-
-
-

Event handling

- -

Most of the onXYZ options have been removed. The same -effect is now obtained by calling -the on method with a string -identifying the event type. Multiple handlers can now be registered -(and individually unregistered) for an event, and objects such as line -handlers now also expose events. See the -full list here.

- -

(The onKeyEvent and onDragEvent options, -which act more as hooks than as event handlers, are still there in -their old form.)

- -
-cm.on("change", function(cm, change) {
-  console.log("something changed! (" + change.origin + ")");
-});
-
-
-
-

markText method arguments

- -

The markText method -(which has gained some interesting new features, such as creating -atomic and read-only spans, or replacing spans with widgets) no longer -takes the CSS class name as a separate argument, but makes it an -optional field in the options object instead.

- -
-// Style first ten lines, and forbid the cursor from entering them
-cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
-  className: "magic-text",
-  inclusiveLeft: true,
-  atomic: true
-});
-
-
-
-

Line folding

- -

The interface for hiding lines has been -removed. markText can -now be used to do the same in a more flexible and powerful way.

- -

The folding script has been -updated to use the new interface, and should now be more robust.

- -
-// Fold a range, replacing it with the text "??"
-var range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {
-  replacedWith: document.createTextNode("??"),
-  // Auto-unfold when cursor moves into the range
-  clearOnEnter: true
-});
-// Get notified when auto-unfolding
-CodeMirror.on(range, "clear", function() {
-  console.log("boom");
-});
-
-
-
-

Line CSS classes

- -

The setLineClass method has been replaced -by addLineClass -and removeLineClass, -which allow more modular control over the classes attached to a line.

- -
-var marked = cm.addLineClass(10, "background", "highlighted-line");
-setTimeout(function() {
-  cm.removeLineClass(marked, "background", "highlighted-line");
-});
-
-
-
-

Position properties

- -

All methods that take or return objects that represent screen -positions now use {left, top, bottom, right} properties -(not always all of them) instead of the {x, y, yBot} used -by some methods in v2.x.

- -

Affected methods -are cursorCoords, charCoords, coordsChar, -and getScrollInfo.

-
-
-

Bracket matching no longer in core

- -

The matchBrackets -option is no longer defined in the core editor. -Load addon/edit/matchbrackets.js to enable it.

-
-
-

Mode management

- -

The CodeMirror.listModes -and CodeMirror.listMIMEs functions, used for listing -defined modes, are gone. You are now encouraged to simply -inspect CodeMirror.modes (mapping mode names to mode -constructors) and CodeMirror.mimeModes (mapping MIME -strings to mode specs).

-
-
-

New features

- -

Some more reasons to upgrade to version 3.

- -
    -
  • Bi-directional text support. CodeMirror will now mostly do the - right thing when editing Arabic or Hebrew text.
  • -
  • Arbitrary line heights. Using fonts with different heights - inside the editor (whether off by one pixel or fifty) is now - supported and handled gracefully.
  • -
  • In-line widgets. See the demo - and the docs.
  • -
  • Defining custom options - with CodeMirror.defineOption.
  • -
-
-
- - diff --git a/CODE/js/codemirror/doc/upgrade_v4.html b/CODE/js/codemirror/doc/upgrade_v4.html deleted file mode 100644 index dc79654f..00000000 --- a/CODE/js/codemirror/doc/upgrade_v4.html +++ /dev/null @@ -1,144 +0,0 @@ - - -CodeMirror: Version 4 upgrade guide - - - - - - -
- -

Upgrading to version 4

- -

CodeMirror 4's interface is very close version 3, but it -does fix a few awkward details in a backwards-incompatible ways. At -least skim the text below before upgrading.

- -

Multiple selections

- -

The main new feature in version 4 is multiple selections. The -single-selection variants of methods are still there, but now -typically act only on the primary selection (usually the last -one added).

- -

The exception to this -is getSelection, -which will now return the content of all selections -(separated by newlines, or whatever lineSep parameter you passed -it).

- -
- -

The beforeSelectionChange event

- -

This event still exists, but the object it is passed has -a completely new -interface, because such changes now concern multiple -selections.

- -
- -

replaceSelection's collapsing behavior

- -

By -default, replaceSelection -would leave the newly inserted text selected. This is only rarely what -you want, and also (slightly) more expensive in the new model, so the -default was changed to "end", meaning the old behavior -must be explicitly specified by passing a second argument -of "around".

- -
- -

change event data

- -

Rather than forcing client code to follow next -pointers from one change object to the next, the library will now -simply fire -multiple "change" -events. Existing code will probably continue to work unmodified.

- -
- -

showIfHidden option to line widgets

- -

This option, which conceptually caused line widgets to be visible -even if their line was hidden, was never really well-defined, and was -buggy from the start. It would be a rather expensive feature, both in -code complexity and run-time performance, to implement properly. It -has been dropped entirely in 4.0.

- -
- -

Module loaders

- -

All modules in the CodeMirror distribution are now wrapped in a -shim function to make them compatible with both AMD -(requirejs) and CommonJS (as used -by node -and browserify) module loaders. -When neither of these is present, they fall back to simply using the -global CodeMirror variable.

- -

If you have a module loader present in your environment, CodeMirror -will attempt to use it, and you might need to change the way you load -CodeMirror modules.

- -
- -

Mutating shared data structures

- -

Data structures produced by the library should not be mutated -unless explicitly allowed, in general. This is slightly more strict in -4.0 than it was in earlier versions, which copied the position objects -returned by getCursor -for nebulous, historic reasons. In 4.0, mutating these -objects will corrupt your editor's selection.

- -
- -

Deprecated interfaces dropped

- -

A few properties and methods that have been deprecated for a while -are now gone. Most notably, the onKeyEvent -and onDragEvent options (use the -corresponding events instead).

- -

Two silly methods, which were mostly there to stay close to the 0.x -API, setLine and removeLine are now gone. -Use the more -flexible replaceRange -method instead.

- -

The long names for folding and completing functions -(CodeMirror.braceRangeFinder, CodeMirror.javascriptHint, -etc) are also gone -(use CodeMirror.fold.brace, CodeMirror.hint.javascript).

- -

The className property in the return value -of getTokenAt, which -has been superseded by the type property, is also no -longer present.

- -
-
diff --git a/CODE/js/codemirror/doc/yinyang.png b/CODE/js/codemirror/doc/yinyang.png deleted file mode 100644 index 2eafd3f1ca137898ede8c27d0e64d99919bdc1ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4633 zcmV+!66WoRP)02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{01<&nL_t(|+U=cPY#Y~=$Nxhy zV#HV?X-TGS*&^w9;jhFRrrmgx#ahFz1vCy?kuL#aq!!t?6h%Z81?uLZB43geHDU@V z+Mp;vst;|SN~%ad0C~-k)9hlmSaX=zPPDc)WGqXTMTJAjrb?Oe-3N*cW$Tk1&I~CX zfUqS?^p$>dKJU5bQiKrHlFYFz0f02eVNnnYpU(;c7Gg1IX}a*adb1fCv$^nbBg0Ur zF`=pzaE9keIvT}1$05D+nvTcOcJd?uKugo8)9VXguh#2PZ#E;%afQz<#A3+AVug?I zT)vD}lL=ayhL)z$$S`Q==;%@#3>UWnOCae;gd_$BF~_o)9vj1Ms}+q511(KMV>Ux; zwTkiJV2))A4^1)jMuvgDyBivdh1v!zk|aYRB7{O14+gQ*Y=+)$hrwdXeFI?DcrCgJCeg6hO_fEbc`jSeTt%_4{{s zbQA)kyswm6vdJW70s&0ER} z6HG1_T6%gU_0>fWEG9f2EX3n5`~7IL*{CfAIK%T~!s|u+4}ZXscizEbB7s{MF084g zrAn*G1f$!XkA5Ww9dkONu~@LDzh4&6k^`Rd`N*|%=U`}RLW|3V*asg}1nf#${cK+! z_If;$KhO!k9}^xA8f`Z0zi>g)yjn8A8J;J%91cv~xPeFMv}|sn)nv+>P04-ql+TAT zpAXLk0x&onRLuaM@Oa4RyYJ%B!h&M5hXXx5*ni;y)CPkrW?S!YU~hka#kE;g;ivZ0 zXJ7w18U5MMkW~;|02mt_#Hh`NY%(e7|J!7<;V8!egy6c>I-HI~NL6ZZIuarO{qoDX zd@PeuY^7&+M+Yo^ziiR=mF{jlH!y$}my4o>KKu}ib91OBkrQ$@nZzi=Kx?(a5(tP2S+xx}CkwN);9V}s zeKG*34F()yS%45+qiK>&CQm5}a5kAdHFo9rYb4vK1)f4+1{@;td%_?{ksqC_#*WdpwU{PU!i;O71D zOR<>bIdUZ+mt~^I&YY<^xOsnk+wGQh4VJ+{WOyFD(@A6je5b#keDUFjH3zrw@c!mE z$nde&R&@_y_y=Y_@)EB{Bh{Z6+vY7CCaj&SqS!^x- zw^T|I7UlEKPBbzM+Pq#$bim1AkbH9XEVB3SOZEdb=yZ5K6xvXa)l48zG0<}7#d*o_ zJpQYr1209R8}8uTkcrMFlczp;;|)pS?xAT|S$4w>bj?mDjz%KbQ*<;%ESY0jMQE_b zV!;uw7crMhTpxVf?H-0&Eh*eX;V>FD39C-8$H8C_4LV&-0tNtMUmpk|n7VLbL#B6K z6FnOalc?P;N#9gnUgkJ7baa&b{qr1$2*Y4rB;TndKfIL5;C~qgFLKu`t~O{XIf#Q@e?tvKLzZzxY!t*5<{pMDB5KQGzh#p-(0 z+wG#RE32@Iozd+^JQzfV=Sj(cW8e9Xq#clZXc{{@JH-Lq-7TswUZ_wSR;s8Ch7AQ= zs{&5={UjxEDNvrWAflPhC=75u;L7kkX2W5!X~2{3y(ej4wJoK)v`Mdba~eDw z4wJq29p5M~Ak%X*LdBO{{1-F^JH;{CAJYDJw+hh#8FRt22; z^Pgos+F7t7PABH0(bCL(G>T8UyG31(lFjq7yx8r2!o@8EJpJomD`v5g&dyCko*5j> zeV@o119Hs#D0Q9Vs{zjNJW1TSqnO3cr&9P&nnuj!!a_X0c6yacVbbf(X?_KEeZ;P7 zS4uP%3wD~#%Nq0wo4jI0uVTxsKmfM_0X*w;LT|T2t=FT0rZLZP$O;04PzV#jU`4sWwPJc~tgwx!0&+c9>FqM%ljhmski93mq_oazEmZMT0FdUmVM?RfCig&p{^;m7)gm#=5UqC z$O;0~3$YloTey3&818Dvc&MfG(ChuXB29E=3v?c?CWn9W6CC9@)amuP`wb{+#aA)N z)&4zyM}pEsy%oW>wzuOzFoO?;9r8GIx-;cxLuwHC+U!5<^C=##lzFlW05m&x z)CAn;d_J@|omI8wN(EkbE?-8I%|=m#5c~&4k!pv#k6~~q9Inc>iyd@jcpjq+gX8fy zl?$?JU_aRG_gC%O#WvM*MKOp90G>HjdUHP0OHGMlm2(KVli+|C$3wqiW%TG8xCWq*}QCq^s<(n_9_Zk#Gae6CVTX zHzAW8hg%{JJuOFsQ0R$L?J~fI*Iuide7I4hvBP<}X}4pt#P+4zEW4?D6e<;L2}46q zJow82I~8Gp0|B4wsFT`RP9~mj@J#Ga8qR%C4h)M6}~~@cA?pD7|!+-%`-}&>{3X^xLeaif# z*9+qsx0SC7xXEUt_LO-+RW(Sn$UR(AZhp*$!^rSFT3jw_(|`fM^uY&;OqtRmo~NqQ z>tPnDg14`;Q&JNhbGg$|{C?D%Oso3dysUdU zWrpX;$48DRZcv(-BlZ^s0iz6q$uj13?&;{jQ4uZ6TDwWvWD?h{R+#<%bpfw?K3HS1 zP{-bV7ixv2O=%H_r`GB9IK;BpWipjkcxh^aMZ)}d!s9_>XXmxc7@4~9ahZ))1K=AMW~CG)+r;V{Phe(d-8x;FCl8$RK02?U^0kT0%8-M4|JaUvSU zvm4yR-`ClRLo5ra&RA!YNyJ<(bPNs_dal+JYBM_)cl!Iu&GYAxRlvm^0MIE2P>X!q zcX0t^!sEdl%jQ~{mSmU7ghqye(e197)~@D%zgTJyZXX!f@MZzE*$%$XPMjbU9H)rh zt&TtdEfV!cEiN6$ltNYloD2qW+wI0tj_cZ}_Lc-Vn@pbiTU*=kq5`JlRkANjPI(+~ z{z~_+vFsE3y`uTxLZ!xFxcK77A1lHiPsd_Nh_uzGRJhq>5;q+V9P#-!4Ywq~`2xt% z_uhk60X@kRRXUY@qe0cE&4%Wlo;AynqMGPp8GHS8a{G^els)K@(icmqy}Z*25CY5K zV96aARJn(p#$JD&O#St*vi4JJbUOS?JYKPgS}M4}SIu_3(}{E>f@9HWS?x7tPr5ts zyWdg9Z+=r&bDc@05UaA2{VQtQV{i|#Y*)GODat6r_Fw#>tEsI`R><*C2vb!$%)8h$ z=gso5Xq4JaI#f}>YJ=h8p^rY29rBI-eo<@GQkv#$GKo=!Dck2(1aKbmv8z{QHP@L` z3cS;aY=!PItQ6dZSPY{!8|qCaQQ?XTxY#GRzx5W>GEMHe%W)WG7{x=*!xcgy{0~jT z*xM^QTv4Lv)gC>{9>`W?!E1jK+DQ6NwaBpA$f45q(qq7r-M@Fbh!mYRl7fUi2B%i$T26kw* zvS{-@hJiT{ki}#ZLXeI`F#dxdfDi&pAh6;ogsRivVlg-z)W438ckMiVdedDKV#qkl z;%{a%CM3L*Fq=x@_PKMo-Peb|GYs_I-8eilLP-TzQVlMa*>ITLId=|suU^F?8S2;? zoeu3T7mRLqMKn5_O5vW*htGfdQ&1FzwwGUq=~usklwf5ATnu{rhd;#KYu9A!+BDhi z&@a8psby!E4xUZoy(U2wOUr#E5Vfa0L^qQBBw3 - -CodeMirror - - - - - - - - - - - - - - - - - -
- -
-

CodeMirror is a versatile text editor - implemented in JavaScript for the browser. It is specialized for - editing code, and comes with a number of language modes and addons - that implement more advanced editing functionality.

- -

A rich programming API and a - CSS theming system are - available for customizing CodeMirror to fit your application, and - extending it with new functionality.

-
- -
-

This is CodeMirror

-
-
- - -
-
- - - -
-
- Get the current version: 5.58.3.
- You can see the code,
- read the release notes,
- or study the user manual. -
-
- Software needs maintenance,
- maintainers need to subsist.
- You can help per month or - once. -
-
- -
- -
-

Features

- -
- -
-

Community

- -

CodeMirror is an open-source project shared under - an MIT license. It is the editor used in the - dev tools for - Firefox, - Chrome, - and Safari, in Light - Table, Adobe - Brackets, Bitbucket, - and many other projects.

- -

Development and bug tracking happens - on github - (alternate git - repository). - Please read these - pointers before submitting a bug. Use pull requests to submit - patches. All contributions must be released under the same MIT - license that CodeMirror uses.

- -

Discussion around the project is done on - a discussion forum. - Announcements related to the project, such as new versions, are - posted in the - forum's "announce" - category. If needed, you can - contact the maintainer - directly. We aim to be an inclusive, welcoming community. To make - that explicit, we have - a code of - conduct that applies to communication around the project.

-
- -
-

Browser support

-

The desktop versions of the following browsers, - in standards mode (HTML5 <!doctype html> - recommended) are supported:

- - - - - - -
Firefoxversion 4 and up
Chromeany version
Safariversion 5.2 and up
Internet Explorer/Edgeversion 8 and up
Operaversion 9 and up
-

Support for modern mobile browsers is experimental. Recent - versions of the iOS browser and Chrome on Android should work - pretty well.

-
- -
- - -

Sponsors

-

These companies support development of this project:

- -
- -
diff --git a/CODE/js/codemirror/package.json b/CODE/js/codemirror/package.json deleted file mode 100644 index a768858e..00000000 --- a/CODE/js/codemirror/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "codemirror", - "version": "5.58.3", - "main": "lib/codemirror.js", - "style": "lib/codemirror.css", - "author": { - "name": "Marijn Haverbeke", - "email": "marijnh@gmail.com", - "url": "http://marijnhaverbeke.nl" - }, - "description": "Full-featured in-browser code editor", - "license": "MIT", - "directories": { - "lib": "./lib" - }, - "scripts": { - "build": "rollup -c", - "watch": "rollup -w -c", - "prepare": "npm run-script build", - "test": "node ./test/run.js", - "lint": "bin/lint" - }, - "devDependencies": { - "@rollup/plugin-buble": "^0.21.3", - "blint": "^1.1.0", - "node-static": "0.7.11", - "puppeteer": "^1.20.0", - "rollup": "^1.26.3" - }, - "bugs": "http://github.com/codemirror/CodeMirror/issues", - "keywords": [ - "JavaScript", - "CodeMirror", - "Editor" - ], - "homepage": "https://codemirror.net", - "repository": { - "type": "git", - "url": "https://github.com/codemirror/CodeMirror.git" - }, - "jspm": { - "directories": {}, - "dependencies": {}, - "devDependencies": {} - }, - "dependencies": {} -} diff --git a/CODE/js/codemirror/rollup.config.js b/CODE/js/codemirror/rollup.config.js deleted file mode 100644 index f50f62fa..00000000 --- a/CODE/js/codemirror/rollup.config.js +++ /dev/null @@ -1,42 +0,0 @@ -import buble from '@rollup/plugin-buble'; - -export default [ - { - input: "src/codemirror.js", - output: { - banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE - -// This is CodeMirror (https://codemirror.net), a code editor -// implemented in JavaScript on top of the browser's DOM. -// -// You can find some technical background for some of the code below -// at http://marijnhaverbeke.nl/blog/#cm-internals . -`, - format: "umd", - file: "lib/codemirror.js", - name: "CodeMirror" - }, - plugins: [ buble({namedFunctionExpressions: false}) ] - }, - { - input: ["src/addon/runmode/runmode-standalone.js"], - output: { - format: "iife", - file: "addon/runmode/runmode-standalone.js", - name: "CodeMirror", - freeze: false, // IE8 doesn't support Object.freeze. - }, - plugins: [ buble({namedFunctionExpressions: false}) ] - }, - { - input: ["src/addon/runmode/runmode.node.js"], - output: { - format: "cjs", - file: "addon/runmode/runmode.node.js", - name: "CodeMirror", - freeze: false, // IE8 doesn't support Object.freeze. - }, - plugins: [ buble({namedFunctionExpressions: false}) ] - }, -]; diff --git a/CODE/js/codemirror/src/addon/runmode/codemirror-standalone.js b/CODE/js/codemirror/src/addon/runmode/codemirror-standalone.js deleted file mode 100644 index b463a5cf..00000000 --- a/CODE/js/codemirror/src/addon/runmode/codemirror-standalone.js +++ /dev/null @@ -1,22 +0,0 @@ -import StringStream from "../../util/StringStream.js" -import * as modeMethods from "../../modes.js" - -// declare global: globalThis, CodeMirror - -// Create a minimal CodeMirror needed to use runMode, and assign to root. -var root = typeof globalThis !== 'undefined' ? globalThis : window -root.CodeMirror = {} - -// Copy StringStream and mode methods into CodeMirror object. -CodeMirror.StringStream = StringStream -for (var exported in modeMethods) CodeMirror[exported] = modeMethods[exported] - -// Minimal default mode. -CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()})) -CodeMirror.defineMIME("text/plain", "null") - -CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min -CodeMirror.splitLines = function(string) { return string.split(/\r?\n|\r/) } - -CodeMirror.defaults = { indentUnit: 2 } -export default CodeMirror diff --git a/CODE/js/codemirror/src/addon/runmode/codemirror.node.js b/CODE/js/codemirror/src/addon/runmode/codemirror.node.js deleted file mode 100644 index 58efc528..00000000 --- a/CODE/js/codemirror/src/addon/runmode/codemirror.node.js +++ /dev/null @@ -1,21 +0,0 @@ -import StringStream from "../../util/StringStream.js" -import * as modeMethods from "../../modes.js" -import {countColumn} from "../../util/misc.js" - -// Copy StringStream and mode methods into exports (CodeMirror) object. -exports.StringStream = StringStream -exports.countColumn = countColumn -for (var exported in modeMethods) exports[exported] = modeMethods[exported] - -// Shim library CodeMirror with the minimal CodeMirror defined above. -require.cache[require.resolve("../../lib/codemirror")] = require.cache[require.resolve("./runmode.node")] -require.cache[require.resolve("../../addon/runmode/runmode")] = require.cache[require.resolve("./runmode.node")] - -// Minimal default mode. -exports.defineMode("null", () => ({token: stream => stream.skipToEnd()})) -exports.defineMIME("text/plain", "null") - -exports.registerHelper = exports.registerGlobalHelper = Math.min -exports.splitLines = function(string) { return string.split(/\r?\n|\r/) } - -exports.defaults = { indentUnit: 2 } diff --git a/CODE/js/codemirror/src/addon/runmode/runmode-standalone.js b/CODE/js/codemirror/src/addon/runmode/runmode-standalone.js deleted file mode 100644 index 0d7aa6bb..00000000 --- a/CODE/js/codemirror/src/addon/runmode/runmode-standalone.js +++ /dev/null @@ -1,2 +0,0 @@ -import "./codemirror-standalone.js" -import "../../../addon/runmode/runmode.js" \ No newline at end of file diff --git a/CODE/js/codemirror/src/addon/runmode/runmode.node.js b/CODE/js/codemirror/src/addon/runmode/runmode.node.js deleted file mode 100644 index 4f2ed817..00000000 --- a/CODE/js/codemirror/src/addon/runmode/runmode.node.js +++ /dev/null @@ -1,2 +0,0 @@ -import "./codemirror.node.js" -import "../../../addon/runmode/runmode.js" \ No newline at end of file diff --git a/CODE/js/codemirror/src/codemirror.js b/CODE/js/codemirror/src/codemirror.js deleted file mode 100644 index 2a2f54e4..00000000 --- a/CODE/js/codemirror/src/codemirror.js +++ /dev/null @@ -1,3 +0,0 @@ -import { CodeMirror } from "./edit/main.js" - -export default CodeMirror diff --git a/CODE/js/codemirror/src/display/Display.js b/CODE/js/codemirror/src/display/Display.js deleted file mode 100644 index d57f00bd..00000000 --- a/CODE/js/codemirror/src/display/Display.js +++ /dev/null @@ -1,110 +0,0 @@ -import { gecko, ie, ie_version, mobile, webkit } from "../util/browser.js" -import { elt, eltP } from "../util/dom.js" -import { scrollerGap } from "../util/misc.js" -import { getGutters, renderGutters } from "./gutters.js" - -// The display handles the DOM integration, both for input reading -// and content drawing. It holds references to DOM nodes and -// display-related state. - -export function Display(place, doc, input, options) { - let d = this - this.input = input - - // Covers bottom-right square when both scrollbars are present. - d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler") - d.scrollbarFiller.setAttribute("cm-not-content", "true") - // Covers bottom of gutter when coverGutterNextToScrollbar is on - // and h scrollbar is present. - d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler") - d.gutterFiller.setAttribute("cm-not-content", "true") - // Will contain the actual code, positioned to cover the viewport. - d.lineDiv = eltP("div", null, "CodeMirror-code") - // Elements are added to these to represent selection and cursors. - d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1") - d.cursorDiv = elt("div", null, "CodeMirror-cursors") - // A visibility: hidden element used to find the size of things. - d.measure = elt("div", null, "CodeMirror-measure") - // When lines outside of the viewport are measured, they are drawn in this. - d.lineMeasure = elt("div", null, "CodeMirror-measure") - // Wraps everything that needs to exist inside the vertically-padded coordinate system - d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], - null, "position: relative; outline: none") - let lines = eltP("div", [d.lineSpace], "CodeMirror-lines") - // Moved around its parent to cover visible view. - d.mover = elt("div", [lines], null, "position: relative") - // Set to the height of the document, allowing scrolling. - d.sizer = elt("div", [d.mover], "CodeMirror-sizer") - d.sizerWidth = null - // Behavior of elts with overflow: auto and padding is - // inconsistent across browsers. This is used to ensure the - // scrollable area is big enough. - d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;") - // Will contain the gutters, if any. - d.gutters = elt("div", null, "CodeMirror-gutters") - d.lineGutter = null - // Actual scrollable element. - d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll") - d.scroller.setAttribute("tabIndex", "-1") - // The element in which the editor lives. - d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror") - - // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) - if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 } - if (!webkit && !(gecko && mobile)) d.scroller.draggable = true - - if (place) { - if (place.appendChild) place.appendChild(d.wrapper) - else place(d.wrapper) - } - - // Current rendered range (may be bigger than the view window). - d.viewFrom = d.viewTo = doc.first - d.reportedViewFrom = d.reportedViewTo = doc.first - // Information about the rendered lines. - d.view = [] - d.renderedView = null - // Holds info about a single rendered line when it was rendered - // for measurement, while not in view. - d.externalMeasured = null - // Empty space (in pixels) above the view - d.viewOffset = 0 - d.lastWrapHeight = d.lastWrapWidth = 0 - d.updateLineNumbers = null - - d.nativeBarWidth = d.barHeight = d.barWidth = 0 - d.scrollbarsClipped = false - - // Used to only resize the line number gutter when necessary (when - // the amount of lines crosses a boundary that makes its width change) - d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null - // Set to true when a non-horizontal-scrolling line widget is - // added. As an optimization, line widget aligning is skipped when - // this is false. - d.alignWidgets = false - - d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null - - // Tracks the maximum line length so that the horizontal scrollbar - // can be kept static when scrolling. - d.maxLine = null - d.maxLineLength = 0 - d.maxLineChanged = false - - // Used for measuring wheel scrolling granularity - d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null - - // True when shift is held down. - d.shift = false - - // Used to track whether anything happened since the context menu - // was opened. - d.selForContextMenu = null - - d.activeTouch = null - - d.gutterSpecs = getGutters(options.gutters, options.lineNumbers) - renderGutters(d) - - input.init(d) -} diff --git a/CODE/js/codemirror/src/display/focus.js b/CODE/js/codemirror/src/display/focus.js deleted file mode 100644 index 0337327e..00000000 --- a/CODE/js/codemirror/src/display/focus.js +++ /dev/null @@ -1,50 +0,0 @@ -import { restartBlink } from "./selection.js" -import { webkit } from "../util/browser.js" -import { addClass, rmClass } from "../util/dom.js" -import { signal } from "../util/event.js" - -export function ensureFocus(cm) { - if (!cm.hasFocus()) { - cm.display.input.focus() - if (!cm.state.focused) onFocus(cm) - } -} - -export function delayBlurEvent(cm) { - cm.state.delayingBlurEvent = true - setTimeout(() => { if (cm.state.delayingBlurEvent) { - cm.state.delayingBlurEvent = false - if (cm.state.focused) onBlur(cm) - } }, 100) -} - -export function onFocus(cm, e) { - if (cm.state.delayingBlurEvent && !cm.state.draggingText) cm.state.delayingBlurEvent = false - - if (cm.options.readOnly == "nocursor") return - if (!cm.state.focused) { - signal(cm, "focus", cm, e) - cm.state.focused = true - addClass(cm.display.wrapper, "CodeMirror-focused") - // This test prevents this from firing when a context - // menu is closed (since the input reset would kill the - // select-all detection hack) - if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { - cm.display.input.reset() - if (webkit) setTimeout(() => cm.display.input.reset(true), 20) // Issue #1730 - } - cm.display.input.receivedFocus() - } - restartBlink(cm) -} -export function onBlur(cm, e) { - if (cm.state.delayingBlurEvent) return - - if (cm.state.focused) { - signal(cm, "blur", cm, e) - cm.state.focused = false - rmClass(cm.display.wrapper, "CodeMirror-focused") - } - clearInterval(cm.display.blinker) - setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150) -} diff --git a/CODE/js/codemirror/src/display/gutters.js b/CODE/js/codemirror/src/display/gutters.js deleted file mode 100644 index b27b6ce7..00000000 --- a/CODE/js/codemirror/src/display/gutters.js +++ /dev/null @@ -1,44 +0,0 @@ -import { elt, removeChildren } from "../util/dom.js" -import { regChange } from "./view_tracking.js" -import { alignHorizontally } from "./line_numbers.js" -import { updateGutterSpace } from "./update_display.js" - -export function getGutters(gutters, lineNumbers) { - let result = [], sawLineNumbers = false - for (let i = 0; i < gutters.length; i++) { - let name = gutters[i], style = null - if (typeof name != "string") { style = name.style; name = name.className } - if (name == "CodeMirror-linenumbers") { - if (!lineNumbers) continue - else sawLineNumbers = true - } - result.push({className: name, style}) - } - if (lineNumbers && !sawLineNumbers) result.push({className: "CodeMirror-linenumbers", style: null}) - return result -} - -// Rebuild the gutter elements, ensure the margin to the left of the -// code matches their width. -export function renderGutters(display) { - let gutters = display.gutters, specs = display.gutterSpecs - removeChildren(gutters) - display.lineGutter = null - for (let i = 0; i < specs.length; ++i) { - let {className, style} = specs[i] - let gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)) - if (style) gElt.style.cssText = style - if (className == "CodeMirror-linenumbers") { - display.lineGutter = gElt - gElt.style.width = (display.lineNumWidth || 1) + "px" - } - } - gutters.style.display = specs.length ? "" : "none" - updateGutterSpace(display) -} - -export function updateGutters(cm) { - renderGutters(cm.display) - regChange(cm) - alignHorizontally(cm) -} diff --git a/CODE/js/codemirror/src/display/highlight_worker.js b/CODE/js/codemirror/src/display/highlight_worker.js deleted file mode 100644 index 60698157..00000000 --- a/CODE/js/codemirror/src/display/highlight_worker.js +++ /dev/null @@ -1,55 +0,0 @@ -import { getContextBefore, highlightLine, processLine } from "../line/highlight.js" -import { copyState } from "../modes.js" -import { bind } from "../util/misc.js" - -import { runInOp } from "./operations.js" -import { regLineChange } from "./view_tracking.js" - -// HIGHLIGHT WORKER - -export function startWorker(cm, time) { - if (cm.doc.highlightFrontier < cm.display.viewTo) - cm.state.highlight.set(time, bind(highlightWorker, cm)) -} - -function highlightWorker(cm) { - let doc = cm.doc - if (doc.highlightFrontier >= cm.display.viewTo) return - let end = +new Date + cm.options.workTime - let context = getContextBefore(cm, doc.highlightFrontier) - let changedLines = [] - - doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), line => { - if (context.line >= cm.display.viewFrom) { // Visible - let oldStyles = line.styles - let resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null - let highlighted = highlightLine(cm, line, context, true) - if (resetState) context.state = resetState - line.styles = highlighted.styles - let oldCls = line.styleClasses, newCls = highlighted.classes - if (newCls) line.styleClasses = newCls - else if (oldCls) line.styleClasses = null - let ischange = !oldStyles || oldStyles.length != line.styles.length || - oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass) - for (let i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i] - if (ischange) changedLines.push(context.line) - line.stateAfter = context.save() - context.nextLine() - } else { - if (line.text.length <= cm.options.maxHighlightLength) - processLine(cm, line.text, context) - line.stateAfter = context.line % 5 == 0 ? context.save() : null - context.nextLine() - } - if (+new Date > end) { - startWorker(cm, cm.options.workDelay) - return true - } - }) - doc.highlightFrontier = context.line - doc.modeFrontier = Math.max(doc.modeFrontier, context.line) - if (changedLines.length) runInOp(cm, () => { - for (let i = 0; i < changedLines.length; i++) - regLineChange(cm, changedLines[i], "text") - }) -} diff --git a/CODE/js/codemirror/src/display/line_numbers.js b/CODE/js/codemirror/src/display/line_numbers.js deleted file mode 100644 index 073cbade..00000000 --- a/CODE/js/codemirror/src/display/line_numbers.js +++ /dev/null @@ -1,48 +0,0 @@ -import { lineNumberFor } from "../line/utils_line.js" -import { compensateForHScroll } from "../measurement/position_measurement.js" -import { elt } from "../util/dom.js" - -import { updateGutterSpace } from "./update_display.js" - -// Re-align line numbers and gutter marks to compensate for -// horizontal scrolling. -export function alignHorizontally(cm) { - let display = cm.display, view = display.view - if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return - let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft - let gutterW = display.gutters.offsetWidth, left = comp + "px" - for (let i = 0; i < view.length; i++) if (!view[i].hidden) { - if (cm.options.fixedGutter) { - if (view[i].gutter) - view[i].gutter.style.left = left - if (view[i].gutterBackground) - view[i].gutterBackground.style.left = left - } - let align = view[i].alignable - if (align) for (let j = 0; j < align.length; j++) - align[j].style.left = left - } - if (cm.options.fixedGutter) - display.gutters.style.left = (comp + gutterW) + "px" -} - -// Used to ensure that the line number gutter is still the right -// size for the current document size. Returns true when an update -// is needed. -export function maybeUpdateLineNumberWidth(cm) { - if (!cm.options.lineNumbers) return false - let doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display - if (last.length != display.lineNumChars) { - let test = display.measure.appendChild(elt("div", [elt("div", last)], - "CodeMirror-linenumber CodeMirror-gutter-elt")) - let innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW - display.lineGutter.style.width = "" - display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1 - display.lineNumWidth = display.lineNumInnerWidth + padding - display.lineNumChars = display.lineNumInnerWidth ? last.length : -1 - display.lineGutter.style.width = display.lineNumWidth + "px" - updateGutterSpace(cm.display) - return true - } - return false -} diff --git a/CODE/js/codemirror/src/display/mode_state.js b/CODE/js/codemirror/src/display/mode_state.js deleted file mode 100644 index 5d8ebf25..00000000 --- a/CODE/js/codemirror/src/display/mode_state.js +++ /dev/null @@ -1,22 +0,0 @@ -import { getMode } from "../modes.js" - -import { startWorker } from "./highlight_worker.js" -import { regChange } from "./view_tracking.js" - -// Used to get the editor into a consistent state again when options change. - -export function loadMode(cm) { - cm.doc.mode = getMode(cm.options, cm.doc.modeOption) - resetModeState(cm) -} - -export function resetModeState(cm) { - cm.doc.iter(line => { - if (line.stateAfter) line.stateAfter = null - if (line.styles) line.styles = null - }) - cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first - startWorker(cm, 100) - cm.state.modeGen++ - if (cm.curOp) regChange(cm) -} diff --git a/CODE/js/codemirror/src/display/operations.js b/CODE/js/codemirror/src/display/operations.js deleted file mode 100644 index 6f3c9d08..00000000 --- a/CODE/js/codemirror/src/display/operations.js +++ /dev/null @@ -1,205 +0,0 @@ -import { clipPos } from "../line/pos.js" -import { findMaxLine } from "../line/spans.js" -import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement.js" -import { signal } from "../util/event.js" -import { activeElt } from "../util/dom.js" -import { finishOperation, pushOperation } from "../util/operation_group.js" - -import { ensureFocus } from "./focus.js" -import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" -import { restartBlink } from "./selection.js" -import { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from "./scrolling.js" -import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display.js" -import { updateHeightsInViewport } from "./update_lines.js" - -// Operations are used to wrap a series of changes to the editor -// state in such a way that each change won't have to update the -// cursor and display (which would be awkward, slow, and -// error-prone). Instead, display updates are batched and then all -// combined and executed at once. - -let nextOpId = 0 -// Start a new operation. -export function startOperation(cm) { - cm.curOp = { - cm: cm, - viewChanged: false, // Flag that indicates that lines might need to be redrawn - startHeight: cm.doc.height, // Used to detect need to update scrollbar - forceUpdate: false, // Used to force a redraw - updateInput: 0, // Whether to reset the input textarea - typing: false, // Whether this reset should be careful to leave existing text (for compositing) - changeObjs: null, // Accumulated changes, for firing change events - cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on - cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already - selectionChanged: false, // Whether the selection needs to be redrawn - updateMaxLine: false, // Set when the widest line needs to be determined anew - scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet - scrollToPos: null, // Used to scroll to a specific position - focus: false, - id: ++nextOpId // Unique ID - } - pushOperation(cm.curOp) -} - -// Finish an operation, updating the display and signalling delayed events -export function endOperation(cm) { - let op = cm.curOp - if (op) finishOperation(op, group => { - for (let i = 0; i < group.ops.length; i++) - group.ops[i].cm.curOp = null - endOperations(group) - }) -} - -// The DOM updates done when an operation finishes are batched so -// that the minimum number of relayouts are required. -function endOperations(group) { - let ops = group.ops - for (let i = 0; i < ops.length; i++) // Read DOM - endOperation_R1(ops[i]) - for (let i = 0; i < ops.length; i++) // Write DOM (maybe) - endOperation_W1(ops[i]) - for (let i = 0; i < ops.length; i++) // Read DOM - endOperation_R2(ops[i]) - for (let i = 0; i < ops.length; i++) // Write DOM (maybe) - endOperation_W2(ops[i]) - for (let i = 0; i < ops.length; i++) // Read DOM - endOperation_finish(ops[i]) -} - -function endOperation_R1(op) { - let cm = op.cm, display = cm.display - maybeClipScrollbars(cm) - if (op.updateMaxLine) findMaxLine(cm) - - op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || - op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || - op.scrollToPos.to.line >= display.viewTo) || - display.maxLineChanged && cm.options.lineWrapping - op.update = op.mustUpdate && - new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate) -} - -function endOperation_W1(op) { - op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update) -} - -function endOperation_R2(op) { - let cm = op.cm, display = cm.display - if (op.updatedDisplay) updateHeightsInViewport(cm) - - op.barMeasure = measureForScrollbars(cm) - - // If the max line changed since it was last measured, measure it, - // and ensure the document's width matches it. - // updateDisplay_W2 will use these properties to do the actual resizing - if (display.maxLineChanged && !cm.options.lineWrapping) { - op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3 - cm.display.sizerWidth = op.adjustWidthTo - op.barMeasure.scrollWidth = - Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth) - op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)) - } - - if (op.updatedDisplay || op.selectionChanged) - op.preparedSelection = display.input.prepareSelection() -} - -function endOperation_W2(op) { - let cm = op.cm - - if (op.adjustWidthTo != null) { - cm.display.sizer.style.minWidth = op.adjustWidthTo + "px" - if (op.maxScrollLeft < cm.doc.scrollLeft) - setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) - cm.display.maxLineChanged = false - } - - let takeFocus = op.focus && op.focus == activeElt() - if (op.preparedSelection) - cm.display.input.showSelection(op.preparedSelection, takeFocus) - if (op.updatedDisplay || op.startHeight != cm.doc.height) - updateScrollbars(cm, op.barMeasure) - if (op.updatedDisplay) - setDocumentHeight(cm, op.barMeasure) - - if (op.selectionChanged) restartBlink(cm) - - if (cm.state.focused && op.updateInput) - cm.display.input.reset(op.typing) - if (takeFocus) ensureFocus(op.cm) -} - -function endOperation_finish(op) { - let cm = op.cm, display = cm.display, doc = cm.doc - - if (op.updatedDisplay) postUpdateDisplay(cm, op.update) - - // Abort mouse wheel delta measurement, when scrolling explicitly - if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) - display.wheelStartX = display.wheelStartY = null - - // Propagate the scroll position to the actual DOM scroller - if (op.scrollTop != null) setScrollTop(cm, op.scrollTop, op.forceScroll) - - if (op.scrollLeft != null) setScrollLeft(cm, op.scrollLeft, true, true) - // If we need to scroll a specific position into view, do so. - if (op.scrollToPos) { - let rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), - clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin) - maybeScrollWindow(cm, rect) - } - - // Fire events for markers that are hidden/unidden by editing or - // undoing - let hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers - if (hidden) for (let i = 0; i < hidden.length; ++i) - if (!hidden[i].lines.length) signal(hidden[i], "hide") - if (unhidden) for (let i = 0; i < unhidden.length; ++i) - if (unhidden[i].lines.length) signal(unhidden[i], "unhide") - - if (display.wrapper.offsetHeight) - doc.scrollTop = cm.display.scroller.scrollTop - - // Fire change events, and delayed event handlers - if (op.changeObjs) - signal(cm, "changes", cm, op.changeObjs) - if (op.update) - op.update.finish() -} - -// Run the given function in an operation -export function runInOp(cm, f) { - if (cm.curOp) return f() - startOperation(cm) - try { return f() } - finally { endOperation(cm) } -} -// Wraps a function in an operation. Returns the wrapped function. -export function operation(cm, f) { - return function() { - if (cm.curOp) return f.apply(cm, arguments) - startOperation(cm) - try { return f.apply(cm, arguments) } - finally { endOperation(cm) } - } -} -// Used to add methods to editor and doc instances, wrapping them in -// operations. -export function methodOp(f) { - return function() { - if (this.curOp) return f.apply(this, arguments) - startOperation(this) - try { return f.apply(this, arguments) } - finally { endOperation(this) } - } -} -export function docMethodOp(f) { - return function() { - let cm = this.cm - if (!cm || cm.curOp) return f.apply(this, arguments) - startOperation(cm) - try { return f.apply(this, arguments) } - finally { endOperation(cm) } - } -} diff --git a/CODE/js/codemirror/src/display/scroll_events.js b/CODE/js/codemirror/src/display/scroll_events.js deleted file mode 100644 index fbed4266..00000000 --- a/CODE/js/codemirror/src/display/scroll_events.js +++ /dev/null @@ -1,115 +0,0 @@ -import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js" -import { e_preventDefault } from "../util/event.js" - -import { updateDisplaySimple } from "./update_display.js" -import { setScrollLeft, updateScrollTop } from "./scrolling.js" - -// Since the delta values reported on mouse wheel events are -// unstandardized between browsers and even browser versions, and -// generally horribly unpredictable, this code starts by measuring -// the scroll effect that the first few mouse wheel events have, -// and, from that, detects the way it can convert deltas to pixel -// offsets afterwards. -// -// The reason we want to know the amount a wheel event will scroll -// is that it gives us a chance to update the display before the -// actual scrolling happens, reducing flickering. - -let wheelSamples = 0, wheelPixelsPerUnit = null -// Fill in a browser-detected starting value on browsers where we -// know one. These don't have to be accurate -- the result of them -// being wrong would just be a slight flicker on the first wheel -// scroll (if it is large enough). -if (ie) wheelPixelsPerUnit = -.53 -else if (gecko) wheelPixelsPerUnit = 15 -else if (chrome) wheelPixelsPerUnit = -.7 -else if (safari) wheelPixelsPerUnit = -1/3 - -function wheelEventDelta(e) { - let dx = e.wheelDeltaX, dy = e.wheelDeltaY - if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail - if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail - else if (dy == null) dy = e.wheelDelta - return {x: dx, y: dy} -} -export function wheelEventPixels(e) { - let delta = wheelEventDelta(e) - delta.x *= wheelPixelsPerUnit - delta.y *= wheelPixelsPerUnit - return delta -} - -export function onScrollWheel(cm, e) { - let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y - - let display = cm.display, scroll = display.scroller - // Quit if there's nothing to scroll here - let canScrollX = scroll.scrollWidth > scroll.clientWidth - let canScrollY = scroll.scrollHeight > scroll.clientHeight - if (!(dx && canScrollX || dy && canScrollY)) return - - // Webkit browsers on OS X abort momentum scrolls when the target - // of the scroll event is removed from the scrollable element. - // This hack (see related code in patchDisplay) makes sure the - // element is kept around. - if (dy && mac && webkit) { - outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { - for (let i = 0; i < view.length; i++) { - if (view[i].node == cur) { - cm.display.currentWheelTarget = cur - break outer - } - } - } - } - - // On some browsers, horizontal scrolling will cause redraws to - // happen before the gutter has been realigned, causing it to - // wriggle around in a most unseemly way. When we have an - // estimated pixels/delta value, we just handle horizontal - // scrolling entirely here. It'll be slightly off from native, but - // better than glitching out. - if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { - if (dy && canScrollY) - updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)) - setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)) - // Only prevent default scrolling if vertical scrolling is - // actually possible. Otherwise, it causes vertical scroll - // jitter on OSX trackpads when deltaX is small and deltaY - // is large (issue #3579) - if (!dy || (dy && canScrollY)) - e_preventDefault(e) - display.wheelStartX = null // Abort measurement, if in progress - return - } - - // 'Project' the visible viewport to cover the area that is being - // scrolled into view (if we know enough to estimate it). - if (dy && wheelPixelsPerUnit != null) { - let pixels = dy * wheelPixelsPerUnit - let top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight - if (pixels < 0) top = Math.max(0, top + pixels - 50) - else bot = Math.min(cm.doc.height, bot + pixels + 50) - updateDisplaySimple(cm, {top: top, bottom: bot}) - } - - if (wheelSamples < 20) { - if (display.wheelStartX == null) { - display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop - display.wheelDX = dx; display.wheelDY = dy - setTimeout(() => { - if (display.wheelStartX == null) return - let movedX = scroll.scrollLeft - display.wheelStartX - let movedY = scroll.scrollTop - display.wheelStartY - let sample = (movedY && display.wheelDY && movedY / display.wheelDY) || - (movedX && display.wheelDX && movedX / display.wheelDX) - display.wheelStartX = display.wheelStartY = null - if (!sample) return - wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1) - ++wheelSamples - }, 200) - } else { - display.wheelDX += dx; display.wheelDY += dy - } - } -} diff --git a/CODE/js/codemirror/src/display/scrollbars.js b/CODE/js/codemirror/src/display/scrollbars.js deleted file mode 100644 index 18ac121a..00000000 --- a/CODE/js/codemirror/src/display/scrollbars.js +++ /dev/null @@ -1,193 +0,0 @@ -import { addClass, elt, rmClass } from "../util/dom.js" -import { on } from "../util/event.js" -import { scrollGap, paddingVert } from "../measurement/position_measurement.js" -import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js" -import { updateHeightsInViewport } from "./update_lines.js" -import { Delayed } from "../util/misc.js" - -import { setScrollLeft, updateScrollTop } from "./scrolling.js" - -// SCROLLBARS - -// Prepare DOM reads needed to update the scrollbars. Done in one -// shot to minimize update/measure roundtrips. -export function measureForScrollbars(cm) { - let d = cm.display, gutterW = d.gutters.offsetWidth - let docH = Math.round(cm.doc.height + paddingVert(cm.display)) - return { - clientHeight: d.scroller.clientHeight, - viewHeight: d.wrapper.clientHeight, - scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, - viewWidth: d.wrapper.clientWidth, - barLeft: cm.options.fixedGutter ? gutterW : 0, - docHeight: docH, - scrollHeight: docH + scrollGap(cm) + d.barHeight, - nativeBarWidth: d.nativeBarWidth, - gutterWidth: gutterW - } -} - -class NativeScrollbars { - constructor(place, scroll, cm) { - this.cm = cm - let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") - let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") - vert.tabIndex = horiz.tabIndex = -1 - place(vert); place(horiz) - - on(vert, "scroll", () => { - if (vert.clientHeight) scroll(vert.scrollTop, "vertical") - }) - on(horiz, "scroll", () => { - if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") - }) - - this.checkedZeroWidth = false - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px" - } - - update(measure) { - let needsH = measure.scrollWidth > measure.clientWidth + 1 - let needsV = measure.scrollHeight > measure.clientHeight + 1 - let sWidth = measure.nativeBarWidth - - if (needsV) { - this.vert.style.display = "block" - this.vert.style.bottom = needsH ? sWidth + "px" : "0" - let totalHeight = measure.viewHeight - (needsH ? sWidth : 0) - // A bug in IE8 can cause this value to be negative, so guard it. - this.vert.firstChild.style.height = - Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px" - } else { - this.vert.style.display = "" - this.vert.firstChild.style.height = "0" - } - - if (needsH) { - this.horiz.style.display = "block" - this.horiz.style.right = needsV ? sWidth + "px" : "0" - this.horiz.style.left = measure.barLeft + "px" - let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) - this.horiz.firstChild.style.width = - Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px" - } else { - this.horiz.style.display = "" - this.horiz.firstChild.style.width = "0" - } - - if (!this.checkedZeroWidth && measure.clientHeight > 0) { - if (sWidth == 0) this.zeroWidthHack() - this.checkedZeroWidth = true - } - - return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} - } - - setScrollLeft(pos) { - if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos - if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz") - } - - setScrollTop(pos) { - if (this.vert.scrollTop != pos) this.vert.scrollTop = pos - if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert, "vert") - } - - zeroWidthHack() { - let w = mac && !mac_geMountainLion ? "12px" : "18px" - this.horiz.style.height = this.vert.style.width = w - this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" - this.disableHoriz = new Delayed - this.disableVert = new Delayed - } - - enableZeroWidthBar(bar, delay, type) { - bar.style.pointerEvents = "auto" - function maybeDisable() { - // To find out whether the scrollbar is still visible, we - // check whether the element under the pixel in the bottom - // right corner of the scrollbar box is the scrollbar box - // itself (when the bar is still visible) or its filler child - // (when the bar is hidden). If it is still visible, we keep - // it enabled, if it's hidden, we disable pointer events. - let box = bar.getBoundingClientRect() - let elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) - : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1) - if (elt != bar) bar.style.pointerEvents = "none" - else delay.set(1000, maybeDisable) - } - delay.set(1000, maybeDisable) - } - - clear() { - let parent = this.horiz.parentNode - parent.removeChild(this.horiz) - parent.removeChild(this.vert) - } -} - -class NullScrollbars { - update() { return {bottom: 0, right: 0} } - setScrollLeft() {} - setScrollTop() {} - clear() {} -} - -export function updateScrollbars(cm, measure) { - if (!measure) measure = measureForScrollbars(cm) - let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight - updateScrollbarsInner(cm, measure) - for (let i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { - if (startWidth != cm.display.barWidth && cm.options.lineWrapping) - updateHeightsInViewport(cm) - updateScrollbarsInner(cm, measureForScrollbars(cm)) - startWidth = cm.display.barWidth; startHeight = cm.display.barHeight - } -} - -// Re-synchronize the fake scrollbars with the actual size of the -// content. -function updateScrollbarsInner(cm, measure) { - let d = cm.display - let sizes = d.scrollbars.update(measure) - - d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" - d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" - d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" - - if (sizes.right && sizes.bottom) { - d.scrollbarFiller.style.display = "block" - d.scrollbarFiller.style.height = sizes.bottom + "px" - d.scrollbarFiller.style.width = sizes.right + "px" - } else d.scrollbarFiller.style.display = "" - if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { - d.gutterFiller.style.display = "block" - d.gutterFiller.style.height = sizes.bottom + "px" - d.gutterFiller.style.width = measure.gutterWidth + "px" - } else d.gutterFiller.style.display = "" -} - -export let scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} - -export function initScrollbars(cm) { - if (cm.display.scrollbars) { - cm.display.scrollbars.clear() - if (cm.display.scrollbars.addClass) - rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) - } - - cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](node => { - cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) - // Prevent clicks in the scrollbars from killing focus - on(node, "mousedown", () => { - if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0) - }) - node.setAttribute("cm-not-content", "true") - }, (pos, axis) => { - if (axis == "horizontal") setScrollLeft(cm, pos) - else updateScrollTop(cm, pos) - }, cm) - if (cm.display.scrollbars.addClass) - addClass(cm.display.wrapper, cm.display.scrollbars.addClass) -} diff --git a/CODE/js/codemirror/src/display/scrolling.js b/CODE/js/codemirror/src/display/scrolling.js deleted file mode 100644 index 75d6fc7e..00000000 --- a/CODE/js/codemirror/src/display/scrolling.js +++ /dev/null @@ -1,185 +0,0 @@ -import { Pos } from "../line/pos.js" -import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement.js" -import { gecko, phantom } from "../util/browser.js" -import { elt } from "../util/dom.js" -import { signalDOMEvent } from "../util/event.js" - -import { startWorker } from "./highlight_worker.js" -import { alignHorizontally } from "./line_numbers.js" -import { updateDisplaySimple } from "./update_display.js" - -// SCROLLING THINGS INTO VIEW - -// If an editor sits on the top or bottom of the window, partially -// scrolled out of view, this ensures that the cursor is visible. -export function maybeScrollWindow(cm, rect) { - if (signalDOMEvent(cm, "scrollCursorIntoView")) return - - let display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null - if (rect.top + box.top < 0) doScroll = true - else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false - if (doScroll != null && !phantom) { - let scrollNode = elt("div", "\u200b", null, `position: absolute; - top: ${rect.top - display.viewOffset - paddingTop(cm.display)}px; - height: ${rect.bottom - rect.top + scrollGap(cm) + display.barHeight}px; - left: ${rect.left}px; width: ${Math.max(2, rect.right - rect.left)}px;`) - cm.display.lineSpace.appendChild(scrollNode) - scrollNode.scrollIntoView(doScroll) - cm.display.lineSpace.removeChild(scrollNode) - } -} - -// Scroll a given position into view (immediately), verifying that -// it actually became visible (as line heights are accurately -// measured, the position of something may 'drift' during drawing). -export function scrollPosIntoView(cm, pos, end, margin) { - if (margin == null) margin = 0 - let rect - if (!cm.options.lineWrapping && pos == end) { - // Set pos and end to the cursor positions around the character pos sticks to - // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch - // If pos == Pos(_, 0, "before"), pos and end are unchanged - pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos - end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos - } - for (let limit = 0; limit < 5; limit++) { - let changed = false - let coords = cursorCoords(cm, pos) - let endCoords = !end || end == pos ? coords : cursorCoords(cm, end) - rect = {left: Math.min(coords.left, endCoords.left), - top: Math.min(coords.top, endCoords.top) - margin, - right: Math.max(coords.left, endCoords.left), - bottom: Math.max(coords.bottom, endCoords.bottom) + margin} - let scrollPos = calculateScrollPos(cm, rect) - let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft - if (scrollPos.scrollTop != null) { - updateScrollTop(cm, scrollPos.scrollTop) - if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true - } - if (scrollPos.scrollLeft != null) { - setScrollLeft(cm, scrollPos.scrollLeft) - if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true - } - if (!changed) break - } - return rect -} - -// Scroll a given set of coordinates into view (immediately). -export function scrollIntoView(cm, rect) { - let scrollPos = calculateScrollPos(cm, rect) - if (scrollPos.scrollTop != null) updateScrollTop(cm, scrollPos.scrollTop) - if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft) -} - -// Calculate a new scroll position needed to scroll the given -// rectangle into view. Returns an object with scrollTop and -// scrollLeft properties. When these are undefined, the -// vertical/horizontal position does not need to be adjusted. -function calculateScrollPos(cm, rect) { - let display = cm.display, snapMargin = textHeight(cm.display) - if (rect.top < 0) rect.top = 0 - let screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop - let screen = displayHeight(cm), result = {} - if (rect.bottom - rect.top > screen) rect.bottom = rect.top + screen - let docBottom = cm.doc.height + paddingVert(display) - let atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin - if (rect.top < screentop) { - result.scrollTop = atTop ? 0 : rect.top - } else if (rect.bottom > screentop + screen) { - let newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen) - if (newTop != screentop) result.scrollTop = newTop - } - - let gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth - let screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace - let screenw = displayWidth(cm) - display.gutters.offsetWidth - let tooWide = rect.right - rect.left > screenw - if (tooWide) rect.right = rect.left + screenw - if (rect.left < 10) - result.scrollLeft = 0 - else if (rect.left < screenleft) - result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)) - else if (rect.right > screenw + screenleft - 3) - result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw - return result -} - -// Store a relative adjustment to the scroll position in the current -// operation (to be applied when the operation finishes). -export function addToScrollTop(cm, top) { - if (top == null) return - resolveScrollToPos(cm) - cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top -} - -// Make sure that at the end of the operation the current cursor is -// shown. -export function ensureCursorVisible(cm) { - resolveScrollToPos(cm) - let cur = cm.getCursor() - cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin} -} - -export function scrollToCoords(cm, x, y) { - if (x != null || y != null) resolveScrollToPos(cm) - if (x != null) cm.curOp.scrollLeft = x - if (y != null) cm.curOp.scrollTop = y -} - -export function scrollToRange(cm, range) { - resolveScrollToPos(cm) - cm.curOp.scrollToPos = range -} - -// When an operation has its scrollToPos property set, and another -// scroll action is applied before the end of the operation, this -// 'simulates' scrolling that position into view in a cheap way, so -// that the effect of intermediate scroll commands is not ignored. -function resolveScrollToPos(cm) { - let range = cm.curOp.scrollToPos - if (range) { - cm.curOp.scrollToPos = null - let from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) - scrollToCoordsRange(cm, from, to, range.margin) - } -} - -export function scrollToCoordsRange(cm, from, to, margin) { - let sPos = calculateScrollPos(cm, { - left: Math.min(from.left, to.left), - top: Math.min(from.top, to.top) - margin, - right: Math.max(from.right, to.right), - bottom: Math.max(from.bottom, to.bottom) + margin - }) - scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop) -} - -// Sync the scrollable area and scrollbars, ensure the viewport -// covers the visible area. -export function updateScrollTop(cm, val) { - if (Math.abs(cm.doc.scrollTop - val) < 2) return - if (!gecko) updateDisplaySimple(cm, {top: val}) - setScrollTop(cm, val, true) - if (gecko) updateDisplaySimple(cm) - startWorker(cm, 100) -} - -export function setScrollTop(cm, val, forceScroll) { - val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)) - if (cm.display.scroller.scrollTop == val && !forceScroll) return - cm.doc.scrollTop = val - cm.display.scrollbars.setScrollTop(val) - if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val -} - -// Sync scroller and scrollbar, ensure the gutter elements are -// aligned. -export function setScrollLeft(cm, val, isScroller, forceScroll) { - val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)) - if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) return - cm.doc.scrollLeft = val - alignHorizontally(cm) - if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val - cm.display.scrollbars.setScrollLeft(val) -} diff --git a/CODE/js/codemirror/src/display/selection.js b/CODE/js/codemirror/src/display/selection.js deleted file mode 100644 index d377a9f4..00000000 --- a/CODE/js/codemirror/src/display/selection.js +++ /dev/null @@ -1,161 +0,0 @@ -import { Pos } from "../line/pos.js" -import { visualLine } from "../line/spans.js" -import { getLine } from "../line/utils_line.js" -import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js" -import { getOrder, iterateBidiSections } from "../util/bidi.js" -import { elt } from "../util/dom.js" -import { onBlur } from "./focus.js" - -export function updateSelection(cm) { - cm.display.input.showSelection(cm.display.input.prepareSelection()) -} - -export function prepareSelection(cm, primary = true) { - let doc = cm.doc, result = {} - let curFragment = result.cursors = document.createDocumentFragment() - let selFragment = result.selection = document.createDocumentFragment() - - for (let i = 0; i < doc.sel.ranges.length; i++) { - if (!primary && i == doc.sel.primIndex) continue - let range = doc.sel.ranges[i] - if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue - let collapsed = range.empty() - if (collapsed || cm.options.showCursorWhenSelecting) - drawSelectionCursor(cm, range.head, curFragment) - if (!collapsed) - drawSelectionRange(cm, range, selFragment) - } - return result -} - -// Draws a cursor for the given range -export function drawSelectionCursor(cm, head, output) { - let pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) - - let cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")) - cursor.style.left = pos.left + "px" - cursor.style.top = pos.top + "px" - cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px" - - if (pos.other) { - // Secondary cursor, shown when on a 'jump' in bi-directional text - let otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")) - otherCursor.style.display = "" - otherCursor.style.left = pos.other.left + "px" - otherCursor.style.top = pos.other.top + "px" - otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px" - } -} - -function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } - -// Draws the given range as a highlighted selection -function drawSelectionRange(cm, range, output) { - let display = cm.display, doc = cm.doc - let fragment = document.createDocumentFragment() - let padding = paddingH(cm.display), leftSide = padding.left - let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right - let docLTR = doc.direction == "ltr" - - function add(left, top, width, bottom) { - if (top < 0) top = 0 - top = Math.round(top) - bottom = Math.round(bottom) - fragment.appendChild(elt("div", null, "CodeMirror-selected", `position: absolute; left: ${left}px; - top: ${top}px; width: ${width == null ? rightSide - left : width}px; - height: ${bottom - top}px`)) - } - - function drawForLine(line, fromArg, toArg) { - let lineObj = getLine(doc, line) - let lineLen = lineObj.text.length - let start, end - function coords(ch, bias) { - return charCoords(cm, Pos(line, ch), "div", lineObj, bias) - } - - function wrapX(pos, dir, side) { - let extent = wrappedLineExtentChar(cm, lineObj, null, pos) - let prop = (dir == "ltr") == (side == "after") ? "left" : "right" - let ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1) - return coords(ch, prop)[prop] - } - - let order = getOrder(lineObj, doc.direction) - iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir, i) => { - let ltr = dir == "ltr" - let fromPos = coords(from, ltr ? "left" : "right") - let toPos = coords(to - 1, ltr ? "right" : "left") - - let openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen - let first = i == 0, last = !order || i == order.length - 1 - if (toPos.top - fromPos.top <= 3) { // Single line - let openLeft = (docLTR ? openStart : openEnd) && first - let openRight = (docLTR ? openEnd : openStart) && last - let left = openLeft ? leftSide : (ltr ? fromPos : toPos).left - let right = openRight ? rightSide : (ltr ? toPos : fromPos).right - add(left, fromPos.top, right - left, fromPos.bottom) - } else { // Multiple lines - let topLeft, topRight, botLeft, botRight - if (ltr) { - topLeft = docLTR && openStart && first ? leftSide : fromPos.left - topRight = docLTR ? rightSide : wrapX(from, dir, "before") - botLeft = docLTR ? leftSide : wrapX(to, dir, "after") - botRight = docLTR && openEnd && last ? rightSide : toPos.right - } else { - topLeft = !docLTR ? leftSide : wrapX(from, dir, "before") - topRight = !docLTR && openStart && first ? rightSide : fromPos.right - botLeft = !docLTR && openEnd && last ? leftSide : toPos.left - botRight = !docLTR ? rightSide : wrapX(to, dir, "after") - } - add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom) - if (fromPos.bottom < toPos.top) add(leftSide, fromPos.bottom, null, toPos.top) - add(botLeft, toPos.top, botRight - botLeft, toPos.bottom) - } - - if (!start || cmpCoords(fromPos, start) < 0) start = fromPos - if (cmpCoords(toPos, start) < 0) start = toPos - if (!end || cmpCoords(fromPos, end) < 0) end = fromPos - if (cmpCoords(toPos, end) < 0) end = toPos - }) - return {start: start, end: end} - } - - let sFrom = range.from(), sTo = range.to() - if (sFrom.line == sTo.line) { - drawForLine(sFrom.line, sFrom.ch, sTo.ch) - } else { - let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) - let singleVLine = visualLine(fromLine) == visualLine(toLine) - let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end - let rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start - if (singleVLine) { - if (leftEnd.top < rightStart.top - 2) { - add(leftEnd.right, leftEnd.top, null, leftEnd.bottom) - add(leftSide, rightStart.top, rightStart.left, rightStart.bottom) - } else { - add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom) - } - } - if (leftEnd.bottom < rightStart.top) - add(leftSide, leftEnd.bottom, null, rightStart.top) - } - - output.appendChild(fragment) -} - -// Cursor-blinking -export function restartBlink(cm) { - if (!cm.state.focused) return - let display = cm.display - clearInterval(display.blinker) - let on = true - display.cursorDiv.style.visibility = "" - if (cm.options.cursorBlinkRate > 0) - display.blinker = setInterval(() => { - if (!cm.hasFocus()) onBlur(cm) - display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden" - }, cm.options.cursorBlinkRate) - else if (cm.options.cursorBlinkRate < 0) - display.cursorDiv.style.visibility = "hidden" -} diff --git a/CODE/js/codemirror/src/display/update_display.js b/CODE/js/codemirror/src/display/update_display.js deleted file mode 100644 index e6c21a0a..00000000 --- a/CODE/js/codemirror/src/display/update_display.js +++ /dev/null @@ -1,263 +0,0 @@ -import { sawCollapsedSpans } from "../line/saw_special_spans.js" -import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans.js" -import { getLine, lineNumberFor } from "../line/utils_line.js" -import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement.js" -import { mac, webkit } from "../util/browser.js" -import { activeElt, removeChildren, contains } from "../util/dom.js" -import { hasHandler, signal } from "../util/event.js" -import { indexOf } from "../util/misc.js" - -import { buildLineElement, updateLineForChanges } from "./update_line.js" -import { startWorker } from "./highlight_worker.js" -import { maybeUpdateLineNumberWidth } from "./line_numbers.js" -import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" -import { updateSelection } from "./selection.js" -import { updateHeightsInViewport, visibleLines } from "./update_lines.js" -import { adjustView, countDirtyView, resetView } from "./view_tracking.js" - -// DISPLAY DRAWING - -export class DisplayUpdate { - constructor(cm, viewport, force) { - let display = cm.display - - this.viewport = viewport - // Store some values that we'll need later (but don't want to force a relayout for) - this.visible = visibleLines(display, cm.doc, viewport) - this.editorIsHidden = !display.wrapper.offsetWidth - this.wrapperHeight = display.wrapper.clientHeight - this.wrapperWidth = display.wrapper.clientWidth - this.oldDisplayWidth = displayWidth(cm) - this.force = force - this.dims = getDimensions(cm) - this.events = [] - } - - signal(emitter, type) { - if (hasHandler(emitter, type)) - this.events.push(arguments) - } - finish() { - for (let i = 0; i < this.events.length; i++) - signal.apply(null, this.events[i]) - } -} - -export function maybeClipScrollbars(cm) { - let display = cm.display - if (!display.scrollbarsClipped && display.scroller.offsetWidth) { - display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth - display.heightForcer.style.height = scrollGap(cm) + "px" - display.sizer.style.marginBottom = -display.nativeBarWidth + "px" - display.sizer.style.borderRightWidth = scrollGap(cm) + "px" - display.scrollbarsClipped = true - } -} - -function selectionSnapshot(cm) { - if (cm.hasFocus()) return null - let active = activeElt() - if (!active || !contains(cm.display.lineDiv, active)) return null - let result = {activeElt: active} - if (window.getSelection) { - let sel = window.getSelection() - if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { - result.anchorNode = sel.anchorNode - result.anchorOffset = sel.anchorOffset - result.focusNode = sel.focusNode - result.focusOffset = sel.focusOffset - } - } - return result -} - -function restoreSelection(snapshot) { - if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) return - snapshot.activeElt.focus() - if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && - snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { - let sel = window.getSelection(), range = document.createRange() - range.setEnd(snapshot.anchorNode, snapshot.anchorOffset) - range.collapse(false) - sel.removeAllRanges() - sel.addRange(range) - sel.extend(snapshot.focusNode, snapshot.focusOffset) - } -} - -// Does the actual updating of the line display. Bails out -// (returning false) when there is nothing to be done and forced is -// false. -export function updateDisplayIfNeeded(cm, update) { - let display = cm.display, doc = cm.doc - - if (update.editorIsHidden) { - resetView(cm) - return false - } - - // Bail out if the visible area is already rendered and nothing changed. - if (!update.force && - update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && - (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && - display.renderedView == display.view && countDirtyView(cm) == 0) - return false - - if (maybeUpdateLineNumberWidth(cm)) { - resetView(cm) - update.dims = getDimensions(cm) - } - - // Compute a suitable new viewport (from & to) - let end = doc.first + doc.size - let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) - let to = Math.min(end, update.visible.to + cm.options.viewportMargin) - if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom) - if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo) - if (sawCollapsedSpans) { - from = visualLineNo(cm.doc, from) - to = visualLineEndNo(cm.doc, to) - } - - let different = from != display.viewFrom || to != display.viewTo || - display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth - adjustView(cm, from, to) - - display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)) - // Position the mover div to align with the current scroll position - cm.display.mover.style.top = display.viewOffset + "px" - - let toUpdate = countDirtyView(cm) - if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && - (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) - return false - - // For big changes, we hide the enclosing element during the - // update, since that speeds up the operations on most browsers. - let selSnapshot = selectionSnapshot(cm) - if (toUpdate > 4) display.lineDiv.style.display = "none" - patchDisplay(cm, display.updateLineNumbers, update.dims) - if (toUpdate > 4) display.lineDiv.style.display = "" - display.renderedView = display.view - // There might have been a widget with a focused element that got - // hidden or updated, if so re-focus it. - restoreSelection(selSnapshot) - - // Prevent selection and cursors from interfering with the scroll - // width and height. - removeChildren(display.cursorDiv) - removeChildren(display.selectionDiv) - display.gutters.style.height = display.sizer.style.minHeight = 0 - - if (different) { - display.lastWrapHeight = update.wrapperHeight - display.lastWrapWidth = update.wrapperWidth - startWorker(cm, 400) - } - - display.updateLineNumbers = null - - return true -} - -export function postUpdateDisplay(cm, update) { - let viewport = update.viewport - - for (let first = true;; first = false) { - if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { - // Clip forced viewport to actual scrollable area. - if (viewport && viewport.top != null) - viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} - // Updated line heights might result in the drawn area not - // actually covering the viewport. Keep looping until it does. - update.visible = visibleLines(cm.display, cm.doc, viewport) - if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) - break - } else if (first) { - update.visible = visibleLines(cm.display, cm.doc, viewport) - } - if (!updateDisplayIfNeeded(cm, update)) break - updateHeightsInViewport(cm) - let barMeasure = measureForScrollbars(cm) - updateSelection(cm) - updateScrollbars(cm, barMeasure) - setDocumentHeight(cm, barMeasure) - update.force = false - } - - update.signal(cm, "update", cm) - if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { - update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo) - cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo - } -} - -export function updateDisplaySimple(cm, viewport) { - let update = new DisplayUpdate(cm, viewport) - if (updateDisplayIfNeeded(cm, update)) { - updateHeightsInViewport(cm) - postUpdateDisplay(cm, update) - let barMeasure = measureForScrollbars(cm) - updateSelection(cm) - updateScrollbars(cm, barMeasure) - setDocumentHeight(cm, barMeasure) - update.finish() - } -} - -// Sync the actual display DOM structure with display.view, removing -// nodes for lines that are no longer in view, and creating the ones -// that are not there yet, and updating the ones that are out of -// date. -function patchDisplay(cm, updateNumbersFrom, dims) { - let display = cm.display, lineNumbers = cm.options.lineNumbers - let container = display.lineDiv, cur = container.firstChild - - function rm(node) { - let next = node.nextSibling - // Works around a throw-scroll bug in OS X Webkit - if (webkit && mac && cm.display.currentWheelTarget == node) - node.style.display = "none" - else - node.parentNode.removeChild(node) - return next - } - - let view = display.view, lineN = display.viewFrom - // Loop over the elements in the view, syncing cur (the DOM nodes - // in display.lineDiv) with the view as we go. - for (let i = 0; i < view.length; i++) { - let lineView = view[i] - if (lineView.hidden) { - } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet - let node = buildLineElement(cm, lineView, lineN, dims) - container.insertBefore(node, cur) - } else { // Already drawn - while (cur != lineView.node) cur = rm(cur) - let updateNumber = lineNumbers && updateNumbersFrom != null && - updateNumbersFrom <= lineN && lineView.lineNumber - if (lineView.changes) { - if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false - updateLineForChanges(cm, lineView, lineN, dims) - } - if (updateNumber) { - removeChildren(lineView.lineNumber) - lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))) - } - cur = lineView.node.nextSibling - } - lineN += lineView.size - } - while (cur) cur = rm(cur) -} - -export function updateGutterSpace(display) { - let width = display.gutters.offsetWidth - display.sizer.style.marginLeft = width + "px" -} - -export function setDocumentHeight(cm, measure) { - cm.display.sizer.style.minHeight = measure.docHeight + "px" - cm.display.heightForcer.style.top = measure.docHeight + "px" - cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px" -} diff --git a/CODE/js/codemirror/src/display/update_line.js b/CODE/js/codemirror/src/display/update_line.js deleted file mode 100644 index 50d781ee..00000000 --- a/CODE/js/codemirror/src/display/update_line.js +++ /dev/null @@ -1,188 +0,0 @@ -import { buildLineContent } from "../line/line_data.js" -import { lineNumberFor } from "../line/utils_line.js" -import { ie, ie_version } from "../util/browser.js" -import { elt, classTest } from "../util/dom.js" -import { signalLater } from "../util/operation_group.js" - -// When an aspect of a line changes, a string is added to -// lineView.changes. This updates the relevant part of the line's -// DOM structure. -export function updateLineForChanges(cm, lineView, lineN, dims) { - for (let j = 0; j < lineView.changes.length; j++) { - let type = lineView.changes[j] - if (type == "text") updateLineText(cm, lineView) - else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims) - else if (type == "class") updateLineClasses(cm, lineView) - else if (type == "widget") updateLineWidgets(cm, lineView, dims) - } - lineView.changes = null -} - -// Lines with gutter elements, widgets or a background class need to -// be wrapped, and have the extra elements added to the wrapper div -function ensureLineWrapped(lineView) { - if (lineView.node == lineView.text) { - lineView.node = elt("div", null, null, "position: relative") - if (lineView.text.parentNode) - lineView.text.parentNode.replaceChild(lineView.node, lineView.text) - lineView.node.appendChild(lineView.text) - if (ie && ie_version < 8) lineView.node.style.zIndex = 2 - } - return lineView.node -} - -function updateLineBackground(cm, lineView) { - let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass - if (cls) cls += " CodeMirror-linebackground" - if (lineView.background) { - if (cls) lineView.background.className = cls - else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null } - } else if (cls) { - let wrap = ensureLineWrapped(lineView) - lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) - cm.display.input.setUneditable(lineView.background) - } -} - -// Wrapper around buildLineContent which will reuse the structure -// in display.externalMeasured when possible. -function getLineContent(cm, lineView) { - let ext = cm.display.externalMeasured - if (ext && ext.line == lineView.line) { - cm.display.externalMeasured = null - lineView.measure = ext.measure - return ext.built - } - return buildLineContent(cm, lineView) -} - -// Redraw the line's text. Interacts with the background and text -// classes because the mode may output tokens that influence these -// classes. -function updateLineText(cm, lineView) { - let cls = lineView.text.className - let built = getLineContent(cm, lineView) - if (lineView.text == lineView.node) lineView.node = built.pre - lineView.text.parentNode.replaceChild(built.pre, lineView.text) - lineView.text = built.pre - if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { - lineView.bgClass = built.bgClass - lineView.textClass = built.textClass - updateLineClasses(cm, lineView) - } else if (cls) { - lineView.text.className = cls - } -} - -function updateLineClasses(cm, lineView) { - updateLineBackground(cm, lineView) - if (lineView.line.wrapClass) - ensureLineWrapped(lineView).className = lineView.line.wrapClass - else if (lineView.node != lineView.text) - lineView.node.className = "" - let textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass - lineView.text.className = textClass || "" -} - -function updateLineGutter(cm, lineView, lineN, dims) { - if (lineView.gutter) { - lineView.node.removeChild(lineView.gutter) - lineView.gutter = null - } - if (lineView.gutterBackground) { - lineView.node.removeChild(lineView.gutterBackground) - lineView.gutterBackground = null - } - if (lineView.line.gutterClass) { - let wrap = ensureLineWrapped(lineView) - lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, - `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px; width: ${dims.gutterTotalWidth}px`) - cm.display.input.setUneditable(lineView.gutterBackground) - wrap.insertBefore(lineView.gutterBackground, lineView.text) - } - let markers = lineView.line.gutterMarkers - if (cm.options.lineNumbers || markers) { - let wrap = ensureLineWrapped(lineView) - let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`) - cm.display.input.setUneditable(gutterWrap) - wrap.insertBefore(gutterWrap, lineView.text) - if (lineView.line.gutterClass) - gutterWrap.className += " " + lineView.line.gutterClass - if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) - lineView.lineNumber = gutterWrap.appendChild( - elt("div", lineNumberFor(cm.options, lineN), - "CodeMirror-linenumber CodeMirror-gutter-elt", - `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`)) - if (markers) for (let k = 0; k < cm.display.gutterSpecs.length; ++k) { - let id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id] - if (found) - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", - `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`)) - } - } -} - -function updateLineWidgets(cm, lineView, dims) { - if (lineView.alignable) lineView.alignable = null - let isWidget = classTest("CodeMirror-linewidget") - for (let node = lineView.node.firstChild, next; node; node = next) { - next = node.nextSibling - if (isWidget.test(node.className)) lineView.node.removeChild(node) - } - insertLineWidgets(cm, lineView, dims) -} - -// Build a line's DOM representation from scratch -export function buildLineElement(cm, lineView, lineN, dims) { - let built = getLineContent(cm, lineView) - lineView.text = lineView.node = built.pre - if (built.bgClass) lineView.bgClass = built.bgClass - if (built.textClass) lineView.textClass = built.textClass - - updateLineClasses(cm, lineView) - updateLineGutter(cm, lineView, lineN, dims) - insertLineWidgets(cm, lineView, dims) - return lineView.node -} - -// A lineView may contain multiple logical lines (when merged by -// collapsed spans). The widgets for all of them need to be drawn. -function insertLineWidgets(cm, lineView, dims) { - insertLineWidgetsFor(cm, lineView.line, lineView, dims, true) - if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) - insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) -} - -function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { - if (!line.widgets) return - let wrap = ensureLineWrapped(lineView) - for (let i = 0, ws = line.widgets; i < ws.length; ++i) { - let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")) - if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true") - positionLineWidget(widget, node, lineView, dims) - cm.display.input.setUneditable(node) - if (allowAbove && widget.above) - wrap.insertBefore(node, lineView.gutter || lineView.text) - else - wrap.appendChild(node) - signalLater(widget, "redraw") - } -} - -function positionLineWidget(widget, node, lineView, dims) { - if (widget.noHScroll) { - ;(lineView.alignable || (lineView.alignable = [])).push(node) - let width = dims.wrapperWidth - node.style.left = dims.fixedPos + "px" - if (!widget.coverGutter) { - width -= dims.gutterTotalWidth - node.style.paddingLeft = dims.gutterTotalWidth + "px" - } - node.style.width = width + "px" - } - if (widget.coverGutter) { - node.style.zIndex = 5 - node.style.position = "relative" - if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px" - } -} diff --git a/CODE/js/codemirror/src/display/update_lines.js b/CODE/js/codemirror/src/display/update_lines.js deleted file mode 100644 index 60c367e4..00000000 --- a/CODE/js/codemirror/src/display/update_lines.js +++ /dev/null @@ -1,76 +0,0 @@ -import { heightAtLine } from "../line/spans.js" -import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" -import { paddingTop, charWidth } from "../measurement/position_measurement.js" -import { ie, ie_version } from "../util/browser.js" - -// Read the actual heights of the rendered lines, and update their -// stored heights to match. -export function updateHeightsInViewport(cm) { - let display = cm.display - let prevBottom = display.lineDiv.offsetTop - for (let i = 0; i < display.view.length; i++) { - let cur = display.view[i], wrapping = cm.options.lineWrapping - let height, width = 0 - if (cur.hidden) continue - if (ie && ie_version < 8) { - let bot = cur.node.offsetTop + cur.node.offsetHeight - height = bot - prevBottom - prevBottom = bot - } else { - let box = cur.node.getBoundingClientRect() - height = box.bottom - box.top - // Check that lines don't extend past the right of the current - // editor width - if (!wrapping && cur.text.firstChild) - width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1 - } - let diff = cur.line.height - height - if (diff > .005 || diff < -.005) { - updateLineHeight(cur.line, height) - updateWidgetHeight(cur.line) - if (cur.rest) for (let j = 0; j < cur.rest.length; j++) - updateWidgetHeight(cur.rest[j]) - } - if (width > cm.display.sizerWidth) { - let chWidth = Math.ceil(width / charWidth(cm.display)) - if (chWidth > cm.display.maxLineLength) { - cm.display.maxLineLength = chWidth - cm.display.maxLine = cur.line - cm.display.maxLineChanged = true - } - } - } -} - -// Read and store the height of line widgets associated with the -// given line. -function updateWidgetHeight(line) { - if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) { - let w = line.widgets[i], parent = w.node.parentNode - if (parent) w.height = parent.offsetHeight - } -} - -// Compute the lines that are visible in a given viewport (defaults -// the the current scroll position). viewport may contain top, -// height, and ensure (see op.scrollToPos) properties. -export function visibleLines(display, doc, viewport) { - let top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop - top = Math.floor(top - paddingTop(display)) - let bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight - - let from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom) - // Ensure is a {from: {line, ch}, to: {line, ch}} object, and - // forces those lines into the viewport (if possible). - if (viewport && viewport.ensure) { - let ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line - if (ensureFrom < from) { - from = ensureFrom - to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight) - } else if (Math.min(ensureTo, doc.lastLine()) >= to) { - from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight) - to = ensureTo - } - } - return {from: from, to: Math.max(to, from + 1)} -} diff --git a/CODE/js/codemirror/src/display/view_tracking.js b/CODE/js/codemirror/src/display/view_tracking.js deleted file mode 100644 index 41464f23..00000000 --- a/CODE/js/codemirror/src/display/view_tracking.js +++ /dev/null @@ -1,153 +0,0 @@ -import { buildViewArray } from "../line/line_data.js" -import { sawCollapsedSpans } from "../line/saw_special_spans.js" -import { visualLineEndNo, visualLineNo } from "../line/spans.js" -import { findViewIndex } from "../measurement/position_measurement.js" -import { indexOf } from "../util/misc.js" - -// Updates the display.view data structure for a given change to the -// document. From and to are in pre-change coordinates. Lendiff is -// the amount of lines added or subtracted by the change. This is -// used for changes that span multiple lines, or change the way -// lines are divided into visual lines. regLineChange (below) -// registers single-line changes. -export function regChange(cm, from, to, lendiff) { - if (from == null) from = cm.doc.first - if (to == null) to = cm.doc.first + cm.doc.size - if (!lendiff) lendiff = 0 - - let display = cm.display - if (lendiff && to < display.viewTo && - (display.updateLineNumbers == null || display.updateLineNumbers > from)) - display.updateLineNumbers = from - - cm.curOp.viewChanged = true - - if (from >= display.viewTo) { // Change after - if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) - resetView(cm) - } else if (to <= display.viewFrom) { // Change before - if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { - resetView(cm) - } else { - display.viewFrom += lendiff - display.viewTo += lendiff - } - } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap - resetView(cm) - } else if (from <= display.viewFrom) { // Top overlap - let cut = viewCuttingPoint(cm, to, to + lendiff, 1) - if (cut) { - display.view = display.view.slice(cut.index) - display.viewFrom = cut.lineN - display.viewTo += lendiff - } else { - resetView(cm) - } - } else if (to >= display.viewTo) { // Bottom overlap - let cut = viewCuttingPoint(cm, from, from, -1) - if (cut) { - display.view = display.view.slice(0, cut.index) - display.viewTo = cut.lineN - } else { - resetView(cm) - } - } else { // Gap in the middle - let cutTop = viewCuttingPoint(cm, from, from, -1) - let cutBot = viewCuttingPoint(cm, to, to + lendiff, 1) - if (cutTop && cutBot) { - display.view = display.view.slice(0, cutTop.index) - .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) - .concat(display.view.slice(cutBot.index)) - display.viewTo += lendiff - } else { - resetView(cm) - } - } - - let ext = display.externalMeasured - if (ext) { - if (to < ext.lineN) - ext.lineN += lendiff - else if (from < ext.lineN + ext.size) - display.externalMeasured = null - } -} - -// Register a change to a single line. Type must be one of "text", -// "gutter", "class", "widget" -export function regLineChange(cm, line, type) { - cm.curOp.viewChanged = true - let display = cm.display, ext = cm.display.externalMeasured - if (ext && line >= ext.lineN && line < ext.lineN + ext.size) - display.externalMeasured = null - - if (line < display.viewFrom || line >= display.viewTo) return - let lineView = display.view[findViewIndex(cm, line)] - if (lineView.node == null) return - let arr = lineView.changes || (lineView.changes = []) - if (indexOf(arr, type) == -1) arr.push(type) -} - -// Clear the view. -export function resetView(cm) { - cm.display.viewFrom = cm.display.viewTo = cm.doc.first - cm.display.view = [] - cm.display.viewOffset = 0 -} - -function viewCuttingPoint(cm, oldN, newN, dir) { - let index = findViewIndex(cm, oldN), diff, view = cm.display.view - if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) - return {index: index, lineN: newN} - let n = cm.display.viewFrom - for (let i = 0; i < index; i++) - n += view[i].size - if (n != oldN) { - if (dir > 0) { - if (index == view.length - 1) return null - diff = (n + view[index].size) - oldN - index++ - } else { - diff = n - oldN - } - oldN += diff; newN += diff - } - while (visualLineNo(cm.doc, newN) != newN) { - if (index == (dir < 0 ? 0 : view.length - 1)) return null - newN += dir * view[index - (dir < 0 ? 1 : 0)].size - index += dir - } - return {index: index, lineN: newN} -} - -// Force the view to cover a given range, adding empty view element -// or clipping off existing ones as needed. -export function adjustView(cm, from, to) { - let display = cm.display, view = display.view - if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { - display.view = buildViewArray(cm, from, to) - display.viewFrom = from - } else { - if (display.viewFrom > from) - display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) - else if (display.viewFrom < from) - display.view = display.view.slice(findViewIndex(cm, from)) - display.viewFrom = from - if (display.viewTo < to) - display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) - else if (display.viewTo > to) - display.view = display.view.slice(0, findViewIndex(cm, to)) - } - display.viewTo = to -} - -// Count the number of lines in the view whose DOM representation is -// out of date (or nonexistent). -export function countDirtyView(cm) { - let view = cm.display.view, dirty = 0 - for (let i = 0; i < view.length; i++) { - let lineView = view[i] - if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty - } - return dirty -} diff --git a/CODE/js/codemirror/src/edit/CodeMirror.js b/CODE/js/codemirror/src/edit/CodeMirror.js deleted file mode 100644 index dc016113..00000000 --- a/CODE/js/codemirror/src/edit/CodeMirror.js +++ /dev/null @@ -1,217 +0,0 @@ -import { Display } from "../display/Display.js" -import { onFocus, onBlur } from "../display/focus.js" -import { maybeUpdateLineNumberWidth } from "../display/line_numbers.js" -import { endOperation, operation, startOperation } from "../display/operations.js" -import { initScrollbars } from "../display/scrollbars.js" -import { onScrollWheel } from "../display/scroll_events.js" -import { setScrollLeft, updateScrollTop } from "../display/scrolling.js" -import { clipPos, Pos } from "../line/pos.js" -import { posFromMouse } from "../measurement/position_measurement.js" -import { eventInWidget } from "../measurement/widgets.js" -import Doc from "../model/Doc.js" -import { attachDoc } from "../model/document_data.js" -import { Range } from "../model/selection.js" -import { extendSelection } from "../model/selection_updates.js" -import { ie, ie_version, mobile, webkit } from "../util/browser.js" -import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event.js" -import { copyObj, Delayed } from "../util/misc.js" - -import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events.js" -import { ensureGlobalHandlers } from "./global_events.js" -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" -import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events.js" -import { themeChanged } from "./utils.js" -import { defaults, optionHandlers, Init } from "./options.js" - -// A CodeMirror instance represents an editor. This is the object -// that user code is usually dealing with. - -export function CodeMirror(place, options) { - if (!(this instanceof CodeMirror)) return new CodeMirror(place, options) - - this.options = options = options ? copyObj(options) : {} - // Determine effective options based on given values and defaults. - copyObj(defaults, options, false) - - let doc = options.value - if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) - else if (options.mode) doc.modeOption = options.mode - this.doc = doc - - let input = new CodeMirror.inputStyles[options.inputStyle](this) - let display = this.display = new Display(place, doc, input, options) - display.wrapper.CodeMirror = this - themeChanged(this) - if (options.lineWrapping) - this.display.wrapper.className += " CodeMirror-wrap" - initScrollbars(this) - - this.state = { - keyMaps: [], // stores maps added by addKeyMap - overlays: [], // highlighting overlays, as added by addOverlay - modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info - overwrite: false, - delayingBlurEvent: false, - focused: false, - suppressEdits: false, // used to disable editing during key handlers when in readOnly mode - pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll - selectingText: false, - draggingText: false, - highlight: new Delayed(), // stores highlight worker timeout - keySeq: null, // Unfinished key sequence - specialChars: null - } - - if (options.autofocus && !mobile) display.input.focus() - - // Override magic textarea content restore that IE sometimes does - // on our hidden textarea on reload - if (ie && ie_version < 11) setTimeout(() => this.display.input.reset(true), 20) - - registerEventHandlers(this) - ensureGlobalHandlers() - - startOperation(this) - this.curOp.forceUpdate = true - attachDoc(this, doc) - - if ((options.autofocus && !mobile) || this.hasFocus()) - setTimeout(() => { - if (this.hasFocus() && !this.state.focused) onFocus(this) - }, 20) - else - onBlur(this) - - for (let opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) - optionHandlers[opt](this, options[opt], Init) - maybeUpdateLineNumberWidth(this) - if (options.finishInit) options.finishInit(this) - for (let i = 0; i < initHooks.length; ++i) initHooks[i](this) - endOperation(this) - // Suppress optimizelegibility in Webkit, since it breaks text - // measuring on line wrapping boundaries. - if (webkit && options.lineWrapping && - getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") - display.lineDiv.style.textRendering = "auto" -} - -// The default configuration options. -CodeMirror.defaults = defaults -// Functions to run when options are changed. -CodeMirror.optionHandlers = optionHandlers - -export default CodeMirror - -// Attach the necessary event handlers when initializing the editor -function registerEventHandlers(cm) { - let d = cm.display - on(d.scroller, "mousedown", operation(cm, onMouseDown)) - // Older IE's will not fire a second mousedown for a double click - if (ie && ie_version < 11) - on(d.scroller, "dblclick", operation(cm, e => { - if (signalDOMEvent(cm, e)) return - let pos = posFromMouse(cm, e) - if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return - e_preventDefault(e) - let word = cm.findWordAt(pos) - extendSelection(cm.doc, word.anchor, word.head) - })) - else - on(d.scroller, "dblclick", e => signalDOMEvent(cm, e) || e_preventDefault(e)) - // Some browsers fire contextmenu *after* opening the menu, at - // which point we can't mess with it anymore. Context menu is - // handled in onMouseDown for these browsers. - on(d.scroller, "contextmenu", e => onContextMenu(cm, e)) - on(d.input.getField(), "contextmenu", e => { - if (!d.scroller.contains(e.target)) onContextMenu(cm, e) - }) - - // Used to suppress mouse event handling when a touch happens - let touchFinished, prevTouch = {end: 0} - function finishTouch() { - if (d.activeTouch) { - touchFinished = setTimeout(() => d.activeTouch = null, 1000) - prevTouch = d.activeTouch - prevTouch.end = +new Date - } - } - function isMouseLikeTouchEvent(e) { - if (e.touches.length != 1) return false - let touch = e.touches[0] - return touch.radiusX <= 1 && touch.radiusY <= 1 - } - function farAway(touch, other) { - if (other.left == null) return true - let dx = other.left - touch.left, dy = other.top - touch.top - return dx * dx + dy * dy > 20 * 20 - } - on(d.scroller, "touchstart", e => { - if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { - d.input.ensurePolled() - clearTimeout(touchFinished) - let now = +new Date - d.activeTouch = {start: now, moved: false, - prev: now - prevTouch.end <= 300 ? prevTouch : null} - if (e.touches.length == 1) { - d.activeTouch.left = e.touches[0].pageX - d.activeTouch.top = e.touches[0].pageY - } - } - }) - on(d.scroller, "touchmove", () => { - if (d.activeTouch) d.activeTouch.moved = true - }) - on(d.scroller, "touchend", e => { - let touch = d.activeTouch - if (touch && !eventInWidget(d, e) && touch.left != null && - !touch.moved && new Date - touch.start < 300) { - let pos = cm.coordsChar(d.activeTouch, "page"), range - if (!touch.prev || farAway(touch, touch.prev)) // Single tap - range = new Range(pos, pos) - else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap - range = cm.findWordAt(pos) - else // Triple tap - range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) - cm.setSelection(range.anchor, range.head) - cm.focus() - e_preventDefault(e) - } - finishTouch() - }) - on(d.scroller, "touchcancel", finishTouch) - - // Sync scrolling between fake scrollbars and real scrollable - // area, ensure viewport is updated when scrolling. - on(d.scroller, "scroll", () => { - if (d.scroller.clientHeight) { - updateScrollTop(cm, d.scroller.scrollTop) - setScrollLeft(cm, d.scroller.scrollLeft, true) - signal(cm, "scroll", cm) - } - }) - - // Listen to wheel events in order to try and update the viewport on time. - on(d.scroller, "mousewheel", e => onScrollWheel(cm, e)) - on(d.scroller, "DOMMouseScroll", e => onScrollWheel(cm, e)) - - // Prevent wrapper from ever scrolling - on(d.wrapper, "scroll", () => d.wrapper.scrollTop = d.wrapper.scrollLeft = 0) - - d.dragFunctions = { - enter: e => {if (!signalDOMEvent(cm, e)) e_stop(e)}, - over: e => {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, - start: e => onDragStart(cm, e), - drop: operation(cm, onDrop), - leave: e => {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} - } - - let inp = d.input.getField() - on(inp, "keyup", e => onKeyUp.call(cm, e)) - on(inp, "keydown", operation(cm, onKeyDown)) - on(inp, "keypress", operation(cm, onKeyPress)) - on(inp, "focus", e => onFocus(cm, e)) - on(inp, "blur", e => onBlur(cm, e)) -} - -let initHooks = [] -CodeMirror.defineInitHook = f => initHooks.push(f) diff --git a/CODE/js/codemirror/src/edit/commands.js b/CODE/js/codemirror/src/edit/commands.js deleted file mode 100644 index 4c4cc4cd..00000000 --- a/CODE/js/codemirror/src/edit/commands.js +++ /dev/null @@ -1,178 +0,0 @@ -import { deleteNearSelection } from "./deleteNearSelection.js" -import { runInOp } from "../display/operations.js" -import { ensureCursorVisible } from "../display/scrolling.js" -import { endOfLine } from "../input/movement.js" -import { clipPos, Pos } from "../line/pos.js" -import { visualLine, visualLineEnd } from "../line/spans.js" -import { getLine, lineNo } from "../line/utils_line.js" -import { Range } from "../model/selection.js" -import { selectAll } from "../model/selection_updates.js" -import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js" -import { getOrder } from "../util/bidi.js" - -// Commands are parameter-less actions that can be performed on an -// editor, mostly used for keybindings. -export let commands = { - selectAll: selectAll, - singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll), - killLine: cm => deleteNearSelection(cm, range => { - if (range.empty()) { - let len = getLine(cm.doc, range.head.line).text.length - if (range.head.ch == len && range.head.line < cm.lastLine()) - return {from: range.head, to: Pos(range.head.line + 1, 0)} - else - return {from: range.head, to: Pos(range.head.line, len)} - } else { - return {from: range.from(), to: range.to()} - } - }), - deleteLine: cm => deleteNearSelection(cm, range => ({ - from: Pos(range.from().line, 0), - to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) - })), - delLineLeft: cm => deleteNearSelection(cm, range => ({ - from: Pos(range.from().line, 0), to: range.from() - })), - delWrappedLineLeft: cm => deleteNearSelection(cm, range => { - let top = cm.charCoords(range.head, "div").top + 5 - let leftPos = cm.coordsChar({left: 0, top: top}, "div") - return {from: leftPos, to: range.from()} - }), - delWrappedLineRight: cm => deleteNearSelection(cm, range => { - let top = cm.charCoords(range.head, "div").top + 5 - let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") - return {from: range.from(), to: rightPos } - }), - undo: cm => cm.undo(), - redo: cm => cm.redo(), - undoSelection: cm => cm.undoSelection(), - redoSelection: cm => cm.redoSelection(), - goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)), - goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())), - goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line), - {origin: "+move", bias: 1} - ), - goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head), - {origin: "+move", bias: 1} - ), - goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line), - {origin: "+move", bias: -1} - ), - goLineRight: cm => cm.extendSelectionsBy(range => { - let top = cm.cursorCoords(range.head, "div").top + 5 - return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") - }, sel_move), - goLineLeft: cm => cm.extendSelectionsBy(range => { - let top = cm.cursorCoords(range.head, "div").top + 5 - return cm.coordsChar({left: 0, top: top}, "div") - }, sel_move), - goLineLeftSmart: cm => cm.extendSelectionsBy(range => { - let top = cm.cursorCoords(range.head, "div").top + 5 - let pos = cm.coordsChar({left: 0, top: top}, "div") - if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) - return pos - }, sel_move), - goLineUp: cm => cm.moveV(-1, "line"), - goLineDown: cm => cm.moveV(1, "line"), - goPageUp: cm => cm.moveV(-1, "page"), - goPageDown: cm => cm.moveV(1, "page"), - goCharLeft: cm => cm.moveH(-1, "char"), - goCharRight: cm => cm.moveH(1, "char"), - goColumnLeft: cm => cm.moveH(-1, "column"), - goColumnRight: cm => cm.moveH(1, "column"), - goWordLeft: cm => cm.moveH(-1, "word"), - goGroupRight: cm => cm.moveH(1, "group"), - goGroupLeft: cm => cm.moveH(-1, "group"), - goWordRight: cm => cm.moveH(1, "word"), - delCharBefore: cm => cm.deleteH(-1, "codepoint"), - delCharAfter: cm => cm.deleteH(1, "char"), - delWordBefore: cm => cm.deleteH(-1, "word"), - delWordAfter: cm => cm.deleteH(1, "word"), - delGroupBefore: cm => cm.deleteH(-1, "group"), - delGroupAfter: cm => cm.deleteH(1, "group"), - indentAuto: cm => cm.indentSelection("smart"), - indentMore: cm => cm.indentSelection("add"), - indentLess: cm => cm.indentSelection("subtract"), - insertTab: cm => cm.replaceSelection("\t"), - insertSoftTab: cm => { - let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize - for (let i = 0; i < ranges.length; i++) { - let pos = ranges[i].from() - let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) - spaces.push(spaceStr(tabSize - col % tabSize)) - } - cm.replaceSelections(spaces) - }, - defaultTab: cm => { - if (cm.somethingSelected()) cm.indentSelection("add") - else cm.execCommand("insertTab") - }, - // Swap the two chars left and right of each selection's head. - // Move cursor behind the two swapped characters afterwards. - // - // Doesn't consider line feeds a character. - // Doesn't scan more than one line above to find a character. - // Doesn't do anything on an empty line. - // Doesn't do anything with non-empty selections. - transposeChars: cm => runInOp(cm, () => { - let ranges = cm.listSelections(), newSel = [] - for (let i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) continue - let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text - if (line) { - if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) - if (cur.ch > 0) { - cur = new Pos(cur.line, cur.ch + 1) - cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), - Pos(cur.line, cur.ch - 2), cur, "+transpose") - } else if (cur.line > cm.doc.first) { - let prev = getLine(cm.doc, cur.line - 1).text - if (prev) { - cur = new Pos(cur.line, 1) - cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + - prev.charAt(prev.length - 1), - Pos(cur.line - 1, prev.length - 1), cur, "+transpose") - } - } - } - newSel.push(new Range(cur, cur)) - } - cm.setSelections(newSel) - }), - newlineAndIndent: cm => runInOp(cm, () => { - let sels = cm.listSelections() - for (let i = sels.length - 1; i >= 0; i--) - cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") - sels = cm.listSelections() - for (let i = 0; i < sels.length; i++) - cm.indentLine(sels[i].from().line, null, true) - ensureCursorVisible(cm) - }), - openLine: cm => cm.replaceSelection("\n", "start"), - toggleOverwrite: cm => cm.toggleOverwrite() -} - - -function lineStart(cm, lineN) { - let line = getLine(cm.doc, lineN) - let visual = visualLine(line) - if (visual != line) lineN = lineNo(visual) - return endOfLine(true, cm, visual, lineN, 1) -} -function lineEnd(cm, lineN) { - let line = getLine(cm.doc, lineN) - let visual = visualLineEnd(line) - if (visual != line) lineN = lineNo(visual) - return endOfLine(true, cm, line, lineN, -1) -} -function lineStartSmart(cm, pos) { - let start = lineStart(cm, pos.line) - let line = getLine(cm.doc, start.line) - let order = getOrder(line, cm.doc.direction) - if (!order || order[0].level == 0) { - let firstNonWS = Math.max(start.ch, line.text.search(/\S/)) - let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch - return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) - } - return start -} diff --git a/CODE/js/codemirror/src/edit/deleteNearSelection.js b/CODE/js/codemirror/src/edit/deleteNearSelection.js deleted file mode 100644 index 82e331a5..00000000 --- a/CODE/js/codemirror/src/edit/deleteNearSelection.js +++ /dev/null @@ -1,30 +0,0 @@ -import { runInOp } from "../display/operations.js" -import { ensureCursorVisible } from "../display/scrolling.js" -import { cmp } from "../line/pos.js" -import { replaceRange } from "../model/changes.js" -import { lst } from "../util/misc.js" - -// Helper for deleting text near the selection(s), used to implement -// backspace, delete, and similar functionality. -export function deleteNearSelection(cm, compute) { - let ranges = cm.doc.sel.ranges, kill = [] - // Build up a set of ranges to kill first, merging overlapping - // ranges. - for (let i = 0; i < ranges.length; i++) { - let toKill = compute(ranges[i]) - while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { - let replaced = kill.pop() - if (cmp(replaced.from, toKill.from) < 0) { - toKill.from = replaced.from - break - } - } - kill.push(toKill) - } - // Next, remove those actual ranges. - runInOp(cm, () => { - for (let i = kill.length - 1; i >= 0; i--) - replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") - ensureCursorVisible(cm) - }) -} diff --git a/CODE/js/codemirror/src/edit/drop_events.js b/CODE/js/codemirror/src/edit/drop_events.js deleted file mode 100644 index f309b2df..00000000 --- a/CODE/js/codemirror/src/edit/drop_events.js +++ /dev/null @@ -1,130 +0,0 @@ -import { drawSelectionCursor } from "../display/selection.js" -import { operation } from "../display/operations.js" -import { clipPos } from "../line/pos.js" -import { posFromMouse } from "../measurement/position_measurement.js" -import { eventInWidget } from "../measurement/widgets.js" -import { makeChange, replaceRange } from "../model/changes.js" -import { changeEnd } from "../model/change_measurement.js" -import { simpleSelection } from "../model/selection.js" -import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates.js" -import { ie, presto, safari } from "../util/browser.js" -import { elt, removeChildrenAndAdd } from "../util/dom.js" -import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event.js" -import { indexOf } from "../util/misc.js" - -// Kludge to work around strange IE behavior where it'll sometimes -// re-fire a series of drag-related events right after the drop (#1551) -let lastDrop = 0 - -export function onDrop(e) { - let cm = this - clearDragCursor(cm) - if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) - return - e_preventDefault(e) - if (ie) lastDrop = +new Date - let pos = posFromMouse(cm, e, true), files = e.dataTransfer.files - if (!pos || cm.isReadOnly()) return - // Might be a file drop, in which case we simply extract the text - // and insert it. - if (files && files.length && window.FileReader && window.File) { - let n = files.length, text = Array(n), read = 0 - const markAsReadAndPasteIfAllFilesAreRead = () => { - if (++read == n) { - operation(cm, () => { - pos = clipPos(cm.doc, pos) - let change = {from: pos, to: pos, - text: cm.doc.splitLines( - text.filter(t => t != null).join(cm.doc.lineSeparator())), - origin: "paste"} - makeChange(cm.doc, change) - setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))) - })() - } - } - const readTextFromFile = (file, i) => { - if (cm.options.allowDropFileTypes && - indexOf(cm.options.allowDropFileTypes, file.type) == -1) { - markAsReadAndPasteIfAllFilesAreRead() - return - } - let reader = new FileReader - reader.onerror = () => markAsReadAndPasteIfAllFilesAreRead() - reader.onload = () => { - let content = reader.result - if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { - markAsReadAndPasteIfAllFilesAreRead() - return - } - text[i] = content - markAsReadAndPasteIfAllFilesAreRead() - } - reader.readAsText(file) - } - for (let i = 0; i < files.length; i++) readTextFromFile(files[i], i) - } else { // Normal drop - // Don't do a replace if the drop happened inside of the selected text. - if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { - cm.state.draggingText(e) - // Ensure the editor is re-focused - setTimeout(() => cm.display.input.focus(), 20) - return - } - try { - let text = e.dataTransfer.getData("Text") - if (text) { - let selected - if (cm.state.draggingText && !cm.state.draggingText.copy) - selected = cm.listSelections() - setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)) - if (selected) for (let i = 0; i < selected.length; ++i) - replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag") - cm.replaceSelection(text, "around", "paste") - cm.display.input.focus() - } - } - catch(e){} - } -} - -export function onDragStart(cm, e) { - if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } - if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return - - e.dataTransfer.setData("Text", cm.getSelection()) - e.dataTransfer.effectAllowed = "copyMove" - - // Use dummy image instead of default browsers image. - // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. - if (e.dataTransfer.setDragImage && !safari) { - let img = elt("img", null, null, "position: fixed; left: 0; top: 0;") - img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" - if (presto) { - img.width = img.height = 1 - cm.display.wrapper.appendChild(img) - // Force a relayout, or Opera won't use our image for some obscure reason - img._top = img.offsetTop - } - e.dataTransfer.setDragImage(img, 0, 0) - if (presto) img.parentNode.removeChild(img) - } -} - -export function onDragOver(cm, e) { - let pos = posFromMouse(cm, e) - if (!pos) return - let frag = document.createDocumentFragment() - drawSelectionCursor(cm, pos, frag) - if (!cm.display.dragCursor) { - cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors") - cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv) - } - removeChildrenAndAdd(cm.display.dragCursor, frag) -} - -export function clearDragCursor(cm) { - if (cm.display.dragCursor) { - cm.display.lineSpace.removeChild(cm.display.dragCursor) - cm.display.dragCursor = null - } -} diff --git a/CODE/js/codemirror/src/edit/fromTextArea.js b/CODE/js/codemirror/src/edit/fromTextArea.js deleted file mode 100644 index 35024c5e..00000000 --- a/CODE/js/codemirror/src/edit/fromTextArea.js +++ /dev/null @@ -1,61 +0,0 @@ -import { CodeMirror } from "./CodeMirror.js" -import { activeElt } from "../util/dom.js" -import { off, on } from "../util/event.js" -import { copyObj } from "../util/misc.js" - -export function fromTextArea(textarea, options) { - options = options ? copyObj(options) : {} - options.value = textarea.value - if (!options.tabindex && textarea.tabIndex) - options.tabindex = textarea.tabIndex - if (!options.placeholder && textarea.placeholder) - options.placeholder = textarea.placeholder - // Set autofocus to true if this textarea is focused, or if it has - // autofocus and no other element is focused. - if (options.autofocus == null) { - let hasFocus = activeElt() - options.autofocus = hasFocus == textarea || - textarea.getAttribute("autofocus") != null && hasFocus == document.body - } - - function save() {textarea.value = cm.getValue()} - - let realSubmit - if (textarea.form) { - on(textarea.form, "submit", save) - // Deplorable hack to make the submit method do the right thing. - if (!options.leaveSubmitMethodAlone) { - let form = textarea.form - realSubmit = form.submit - try { - let wrappedSubmit = form.submit = () => { - save() - form.submit = realSubmit - form.submit() - form.submit = wrappedSubmit - } - } catch(e) {} - } - } - - options.finishInit = cm => { - cm.save = save - cm.getTextArea = () => textarea - cm.toTextArea = () => { - cm.toTextArea = isNaN // Prevent this from being ran twice - save() - textarea.parentNode.removeChild(cm.getWrapperElement()) - textarea.style.display = "" - if (textarea.form) { - off(textarea.form, "submit", save) - if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") - textarea.form.submit = realSubmit - } - } - } - - textarea.style.display = "none" - let cm = CodeMirror(node => textarea.parentNode.insertBefore(node, textarea.nextSibling), - options) - return cm -} diff --git a/CODE/js/codemirror/src/edit/global_events.js b/CODE/js/codemirror/src/edit/global_events.js deleted file mode 100644 index d03da2d0..00000000 --- a/CODE/js/codemirror/src/edit/global_events.js +++ /dev/null @@ -1,45 +0,0 @@ -import { onBlur } from "../display/focus.js" -import { on } from "../util/event.js" - -// These must be handled carefully, because naively registering a -// handler for each editor will cause the editors to never be -// garbage collected. - -function forEachCodeMirror(f) { - if (!document.getElementsByClassName) return - let byClass = document.getElementsByClassName("CodeMirror"), editors = [] - for (let i = 0; i < byClass.length; i++) { - let cm = byClass[i].CodeMirror - if (cm) editors.push(cm) - } - if (editors.length) editors[0].operation(() => { - for (let i = 0; i < editors.length; i++) f(editors[i]) - }) -} - -let globalsRegistered = false -export function ensureGlobalHandlers() { - if (globalsRegistered) return - registerGlobalHandlers() - globalsRegistered = true -} -function registerGlobalHandlers() { - // When the window resizes, we need to refresh active editors. - let resizeTimer - on(window, "resize", () => { - if (resizeTimer == null) resizeTimer = setTimeout(() => { - resizeTimer = null - forEachCodeMirror(onResize) - }, 100) - }) - // When the window loses focus, we want to show the editor as blurred - on(window, "blur", () => forEachCodeMirror(onBlur)) -} -// Called when the window resizes -function onResize(cm) { - let d = cm.display - // Might be a text scaling operation, clear size caches. - d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null - d.scrollbarsClipped = false - cm.setSize() -} diff --git a/CODE/js/codemirror/src/edit/key_events.js b/CODE/js/codemirror/src/edit/key_events.js deleted file mode 100644 index 8f72182a..00000000 --- a/CODE/js/codemirror/src/edit/key_events.js +++ /dev/null @@ -1,163 +0,0 @@ -import { signalLater } from "../util/operation_group.js" -import { restartBlink } from "../display/selection.js" -import { isModifierKey, keyName, lookupKey } from "../input/keymap.js" -import { eventInWidget } from "../measurement/widgets.js" -import { ie, ie_version, mac, presto, gecko } from "../util/browser.js" -import { activeElt, addClass, rmClass } from "../util/dom.js" -import { e_preventDefault, off, on, signalDOMEvent } from "../util/event.js" -import { hasCopyEvent } from "../util/feature_detection.js" -import { Delayed, Pass } from "../util/misc.js" - -import { commands } from "./commands.js" - -// Run a handler that was bound to a key. -function doHandleBinding(cm, bound, dropShift) { - if (typeof bound == "string") { - bound = commands[bound] - if (!bound) return false - } - // Ensure previous input has been read, so that the handler sees a - // consistent view of the document - cm.display.input.ensurePolled() - let prevShift = cm.display.shift, done = false - try { - if (cm.isReadOnly()) cm.state.suppressEdits = true - if (dropShift) cm.display.shift = false - done = bound(cm) != Pass - } finally { - cm.display.shift = prevShift - cm.state.suppressEdits = false - } - return done -} - -function lookupKeyForEditor(cm, name, handle) { - for (let i = 0; i < cm.state.keyMaps.length; i++) { - let result = lookupKey(name, cm.state.keyMaps[i], handle, cm) - if (result) return result - } - return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) - || lookupKey(name, cm.options.keyMap, handle, cm) -} - -// Note that, despite the name, this function is also used to check -// for bound mouse clicks. - -let stopSeq = new Delayed - -export function dispatchKey(cm, name, e, handle) { - let seq = cm.state.keySeq - if (seq) { - if (isModifierKey(name)) return "handled" - if (/\'$/.test(name)) - cm.state.keySeq = null - else - stopSeq.set(50, () => { - if (cm.state.keySeq == seq) { - cm.state.keySeq = null - cm.display.input.reset() - } - }) - if (dispatchKeyInner(cm, seq + " " + name, e, handle)) return true - } - return dispatchKeyInner(cm, name, e, handle) -} - -function dispatchKeyInner(cm, name, e, handle) { - let result = lookupKeyForEditor(cm, name, handle) - - if (result == "multi") - cm.state.keySeq = name - if (result == "handled") - signalLater(cm, "keyHandled", cm, name, e) - - if (result == "handled" || result == "multi") { - e_preventDefault(e) - restartBlink(cm) - } - - return !!result -} - -// Handle a key from the keydown event. -function handleKeyBinding(cm, e) { - let name = keyName(e, true) - if (!name) return false - - if (e.shiftKey && !cm.state.keySeq) { - // First try to resolve full name (including 'Shift-'). Failing - // that, see if there is a cursor-motion command (starting with - // 'go') bound to the keyname without 'Shift-'. - return dispatchKey(cm, "Shift-" + name, e, b => doHandleBinding(cm, b, true)) - || dispatchKey(cm, name, e, b => { - if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) - return doHandleBinding(cm, b) - }) - } else { - return dispatchKey(cm, name, e, b => doHandleBinding(cm, b)) - } -} - -// Handle a key from the keypress event -function handleCharBinding(cm, e, ch) { - return dispatchKey(cm, "'" + ch + "'", e, b => doHandleBinding(cm, b, true)) -} - -let lastStoppedKey = null -export function onKeyDown(e) { - let cm = this - if (e.target && e.target != cm.display.input.getField()) return - cm.curOp.focus = activeElt() - if (signalDOMEvent(cm, e)) return - // IE does strange things with escape. - if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false - let code = e.keyCode - cm.display.shift = code == 16 || e.shiftKey - let handled = handleKeyBinding(cm, e) - if (presto) { - lastStoppedKey = handled ? code : null - // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) - cm.replaceSelection("", null, "cut") - } - if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) - document.execCommand("cut") - - // Turn mouse into crosshair when Alt is held on Mac. - if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) - showCrossHair(cm) -} - -function showCrossHair(cm) { - let lineDiv = cm.display.lineDiv - addClass(lineDiv, "CodeMirror-crosshair") - - function up(e) { - if (e.keyCode == 18 || !e.altKey) { - rmClass(lineDiv, "CodeMirror-crosshair") - off(document, "keyup", up) - off(document, "mouseover", up) - } - } - on(document, "keyup", up) - on(document, "mouseover", up) -} - -export function onKeyUp(e) { - if (e.keyCode == 16) this.doc.sel.shift = false - signalDOMEvent(this, e) -} - -export function onKeyPress(e) { - let cm = this - if (e.target && e.target != cm.display.input.getField()) return - if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return - let keyCode = e.keyCode, charCode = e.charCode - if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} - if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return - let ch = String.fromCharCode(charCode == null ? keyCode : charCode) - // Some browsers fire keypress events for backspace - if (ch == "\x08") return - if (handleCharBinding(cm, e, ch)) return - cm.display.input.onKeyPress(e) -} diff --git a/CODE/js/codemirror/src/edit/legacy.js b/CODE/js/codemirror/src/edit/legacy.js deleted file mode 100644 index 889badbe..00000000 --- a/CODE/js/codemirror/src/edit/legacy.js +++ /dev/null @@ -1,62 +0,0 @@ -import { scrollbarModel } from "../display/scrollbars.js" -import { wheelEventPixels } from "../display/scroll_events.js" -import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap.js" -import { keyNames } from "../input/keynames.js" -import { Line } from "../line/line_data.js" -import { cmp, Pos } from "../line/pos.js" -import { changeEnd } from "../model/change_measurement.js" -import Doc from "../model/Doc.js" -import { LineWidget } from "../model/line_widget.js" -import { SharedTextMarker, TextMarker } from "../model/mark_text.js" -import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes.js" -import { addClass, contains, rmClass } from "../util/dom.js" -import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event.js" -import { splitLinesAuto } from "../util/feature_detection.js" -import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc.js" -import StringStream from "../util/StringStream.js" - -import { commands } from "./commands.js" - -export function addLegacyProps(CodeMirror) { - CodeMirror.off = off - CodeMirror.on = on - CodeMirror.wheelEventPixels = wheelEventPixels - CodeMirror.Doc = Doc - CodeMirror.splitLines = splitLinesAuto - CodeMirror.countColumn = countColumn - CodeMirror.findColumn = findColumn - CodeMirror.isWordChar = isWordCharBasic - CodeMirror.Pass = Pass - CodeMirror.signal = signal - CodeMirror.Line = Line - CodeMirror.changeEnd = changeEnd - CodeMirror.scrollbarModel = scrollbarModel - CodeMirror.Pos = Pos - CodeMirror.cmpPos = cmp - CodeMirror.modes = modes - CodeMirror.mimeModes = mimeModes - CodeMirror.resolveMode = resolveMode - CodeMirror.getMode = getMode - CodeMirror.modeExtensions = modeExtensions - CodeMirror.extendMode = extendMode - CodeMirror.copyState = copyState - CodeMirror.startState = startState - CodeMirror.innerMode = innerMode - CodeMirror.commands = commands - CodeMirror.keyMap = keyMap - CodeMirror.keyName = keyName - CodeMirror.isModifierKey = isModifierKey - CodeMirror.lookupKey = lookupKey - CodeMirror.normalizeKeyMap = normalizeKeyMap - CodeMirror.StringStream = StringStream - CodeMirror.SharedTextMarker = SharedTextMarker - CodeMirror.TextMarker = TextMarker - CodeMirror.LineWidget = LineWidget - CodeMirror.e_preventDefault = e_preventDefault - CodeMirror.e_stopPropagation = e_stopPropagation - CodeMirror.e_stop = e_stop - CodeMirror.addClass = addClass - CodeMirror.contains = contains - CodeMirror.rmClass = rmClass - CodeMirror.keyNames = keyNames -} diff --git a/CODE/js/codemirror/src/edit/main.js b/CODE/js/codemirror/src/edit/main.js deleted file mode 100644 index d51192c6..00000000 --- a/CODE/js/codemirror/src/edit/main.js +++ /dev/null @@ -1,69 +0,0 @@ -// EDITOR CONSTRUCTOR - -import { CodeMirror } from "./CodeMirror.js" -export { CodeMirror } from "./CodeMirror.js" - -import { eventMixin } from "../util/event.js" -import { indexOf } from "../util/misc.js" - -import { defineOptions } from "./options.js" - -defineOptions(CodeMirror) - -import addEditorMethods from "./methods.js" - -addEditorMethods(CodeMirror) - -import Doc from "../model/Doc.js" - -// Set up methods on CodeMirror's prototype to redirect to the editor's document. -let dontDelegate = "iter insert remove copy getEditor constructor".split(" ") -for (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) - CodeMirror.prototype[prop] = (function(method) { - return function() {return method.apply(this.doc, arguments)} - })(Doc.prototype[prop]) - -eventMixin(Doc) - -// INPUT HANDLING - -import ContentEditableInput from "../input/ContentEditableInput.js" -import TextareaInput from "../input/TextareaInput.js" -CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} - -// MODE DEFINITION AND QUERYING - -import { defineMIME, defineMode } from "../modes.js" - -// Extra arguments are stored as the mode's dependencies, which is -// used by (legacy) mechanisms like loadmode.js to automatically -// load a mode. (Preferred mechanism is the require/define calls.) -CodeMirror.defineMode = function(name/*, mode, …*/) { - if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name - defineMode.apply(this, arguments) -} - -CodeMirror.defineMIME = defineMIME - -// Minimal default mode. -CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()})) -CodeMirror.defineMIME("text/plain", "null") - -// EXTENSIONS - -CodeMirror.defineExtension = (name, func) => { - CodeMirror.prototype[name] = func -} -CodeMirror.defineDocExtension = (name, func) => { - Doc.prototype[name] = func -} - -import { fromTextArea } from "./fromTextArea.js" - -CodeMirror.fromTextArea = fromTextArea - -import { addLegacyProps } from "./legacy.js" - -addLegacyProps(CodeMirror) - -CodeMirror.version = "5.58.3" diff --git a/CODE/js/codemirror/src/edit/methods.js b/CODE/js/codemirror/src/edit/methods.js deleted file mode 100644 index a5e2afc3..00000000 --- a/CODE/js/codemirror/src/edit/methods.js +++ /dev/null @@ -1,552 +0,0 @@ -import { deleteNearSelection } from "./deleteNearSelection.js" -import { commands } from "./commands.js" -import { attachDoc } from "../model/document_data.js" -import { activeElt, addClass, rmClass } from "../util/dom.js" -import { eventMixin, signal } from "../util/event.js" -import { getLineStyles, getContextBefore, takeToken } from "../line/highlight.js" -import { indentLine } from "../input/indent.js" -import { triggerElectric } from "../input/input.js" -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" -import { onMouseDown } from "./mouse_events.js" -import { getKeyMap } from "../input/keymap.js" -import { endOfLine, moveLogically, moveVisually } from "../input/movement.js" -import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations.js" -import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos.js" -import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement.js" -import { Range } from "../model/selection.js" -import { replaceOneSelection, skipAtomic } from "../model/selection_updates.js" -import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling.js" -import { heightAtLine } from "../line/spans.js" -import { updateGutterSpace } from "../display/update_display.js" -import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" -import { getLine, isLine, lineAtHeight } from "../line/utils_line.js" -import { regChange, regLineChange } from "../display/view_tracking.js" - -// The publicly visible API. Note that methodOp(f) means -// 'wrap f in an operation, performed on its `this` parameter'. - -// This is not the complete set of editor methods. Most of the -// methods defined on the Doc type are also injected into -// CodeMirror.prototype, for backwards compatibility and -// convenience. - -export default function(CodeMirror) { - let optionHandlers = CodeMirror.optionHandlers - - let helpers = CodeMirror.helpers = {} - - CodeMirror.prototype = { - constructor: CodeMirror, - focus: function(){window.focus(); this.display.input.focus()}, - - setOption: function(option, value) { - let options = this.options, old = options[option] - if (options[option] == value && option != "mode") return - options[option] = value - if (optionHandlers.hasOwnProperty(option)) - operation(this, optionHandlers[option])(this, value, old) - signal(this, "optionChange", this, option) - }, - - getOption: function(option) {return this.options[option]}, - getDoc: function() {return this.doc}, - - addKeyMap: function(map, bottom) { - this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) - }, - removeKeyMap: function(map) { - let maps = this.state.keyMaps - for (let i = 0; i < maps.length; ++i) - if (maps[i] == map || maps[i].name == map) { - maps.splice(i, 1) - return true - } - }, - - addOverlay: methodOp(function(spec, options) { - let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) - if (mode.startState) throw new Error("Overlays may not be stateful.") - insertSorted(this.state.overlays, - {mode: mode, modeSpec: spec, opaque: options && options.opaque, - priority: (options && options.priority) || 0}, - overlay => overlay.priority) - this.state.modeGen++ - regChange(this) - }), - removeOverlay: methodOp(function(spec) { - let overlays = this.state.overlays - for (let i = 0; i < overlays.length; ++i) { - let cur = overlays[i].modeSpec - if (cur == spec || typeof spec == "string" && cur.name == spec) { - overlays.splice(i, 1) - this.state.modeGen++ - regChange(this) - return - } - } - }), - - indentLine: methodOp(function(n, dir, aggressive) { - if (typeof dir != "string" && typeof dir != "number") { - if (dir == null) dir = this.options.smartIndent ? "smart" : "prev" - else dir = dir ? "add" : "subtract" - } - if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive) - }), - indentSelection: methodOp(function(how) { - let ranges = this.doc.sel.ranges, end = -1 - for (let i = 0; i < ranges.length; i++) { - let range = ranges[i] - if (!range.empty()) { - let from = range.from(), to = range.to() - let start = Math.max(end, from.line) - end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 - for (let j = start; j < end; ++j) - indentLine(this, j, how) - let newRanges = this.doc.sel.ranges - if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) - replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) - } else if (range.head.line > end) { - indentLine(this, range.head.line, how, true) - end = range.head.line - if (i == this.doc.sel.primIndex) ensureCursorVisible(this) - } - } - }), - - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - getTokenAt: function(pos, precise) { - return takeToken(this, pos, precise) - }, - - getLineTokens: function(line, precise) { - return takeToken(this, Pos(line), precise, true) - }, - - getTokenTypeAt: function(pos) { - pos = clipPos(this.doc, pos) - let styles = getLineStyles(this, getLine(this.doc, pos.line)) - let before = 0, after = (styles.length - 1) / 2, ch = pos.ch - let type - if (ch == 0) type = styles[2] - else for (;;) { - let mid = (before + after) >> 1 - if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid - else if (styles[mid * 2 + 1] < ch) before = mid + 1 - else { type = styles[mid * 2 + 2]; break } - } - let cut = type ? type.indexOf("overlay ") : -1 - return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) - }, - - getModeAt: function(pos) { - let mode = this.doc.mode - if (!mode.innerMode) return mode - return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode - }, - - getHelper: function(pos, type) { - return this.getHelpers(pos, type)[0] - }, - - getHelpers: function(pos, type) { - let found = [] - if (!helpers.hasOwnProperty(type)) return found - let help = helpers[type], mode = this.getModeAt(pos) - if (typeof mode[type] == "string") { - if (help[mode[type]]) found.push(help[mode[type]]) - } else if (mode[type]) { - for (let i = 0; i < mode[type].length; i++) { - let val = help[mode[type][i]] - if (val) found.push(val) - } - } else if (mode.helperType && help[mode.helperType]) { - found.push(help[mode.helperType]) - } else if (help[mode.name]) { - found.push(help[mode.name]) - } - for (let i = 0; i < help._global.length; i++) { - let cur = help._global[i] - if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) - found.push(cur.val) - } - return found - }, - - getStateAfter: function(line, precise) { - let doc = this.doc - line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) - return getContextBefore(this, line + 1, precise).state - }, - - cursorCoords: function(start, mode) { - let pos, range = this.doc.sel.primary() - if (start == null) pos = range.head - else if (typeof start == "object") pos = clipPos(this.doc, start) - else pos = start ? range.from() : range.to() - return cursorCoords(this, pos, mode || "page") - }, - - charCoords: function(pos, mode) { - return charCoords(this, clipPos(this.doc, pos), mode || "page") - }, - - coordsChar: function(coords, mode) { - coords = fromCoordSystem(this, coords, mode || "page") - return coordsChar(this, coords.left, coords.top) - }, - - lineAtHeight: function(height, mode) { - height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top - return lineAtHeight(this.doc, height + this.display.viewOffset) - }, - heightAtLine: function(line, mode, includeWidgets) { - let end = false, lineObj - if (typeof line == "number") { - let last = this.doc.first + this.doc.size - 1 - if (line < this.doc.first) line = this.doc.first - else if (line > last) { line = last; end = true } - lineObj = getLine(this.doc, line) - } else { - lineObj = line - } - return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + - (end ? this.doc.height - heightAtLine(lineObj) : 0) - }, - - defaultTextHeight: function() { return textHeight(this.display) }, - defaultCharWidth: function() { return charWidth(this.display) }, - - getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, - - addWidget: function(pos, node, scroll, vert, horiz) { - let display = this.display - pos = cursorCoords(this, clipPos(this.doc, pos)) - let top = pos.bottom, left = pos.left - node.style.position = "absolute" - node.setAttribute("cm-ignore-events", "true") - this.display.input.setUneditable(node) - display.sizer.appendChild(node) - if (vert == "over") { - top = pos.top - } else if (vert == "above" || vert == "near") { - let vspace = Math.max(display.wrapper.clientHeight, this.doc.height), - hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) - // Default to positioning above (if specified and possible); otherwise default to positioning below - if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) - top = pos.top - node.offsetHeight - else if (pos.bottom + node.offsetHeight <= vspace) - top = pos.bottom - if (left + node.offsetWidth > hspace) - left = hspace - node.offsetWidth - } - node.style.top = top + "px" - node.style.left = node.style.right = "" - if (horiz == "right") { - left = display.sizer.clientWidth - node.offsetWidth - node.style.right = "0px" - } else { - if (horiz == "left") left = 0 - else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2 - node.style.left = left + "px" - } - if (scroll) - scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}) - }, - - triggerOnKeyDown: methodOp(onKeyDown), - triggerOnKeyPress: methodOp(onKeyPress), - triggerOnKeyUp: onKeyUp, - triggerOnMouseDown: methodOp(onMouseDown), - - execCommand: function(cmd) { - if (commands.hasOwnProperty(cmd)) - return commands[cmd].call(null, this) - }, - - triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), - - findPosH: function(from, amount, unit, visually) { - let dir = 1 - if (amount < 0) { dir = -1; amount = -amount } - let cur = clipPos(this.doc, from) - for (let i = 0; i < amount; ++i) { - cur = findPosH(this.doc, cur, dir, unit, visually) - if (cur.hitSide) break - } - return cur - }, - - moveH: methodOp(function(dir, unit) { - this.extendSelectionsBy(range => { - if (this.display.shift || this.doc.extend || range.empty()) - return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually) - else - return dir < 0 ? range.from() : range.to() - }, sel_move) - }), - - deleteH: methodOp(function(dir, unit) { - let sel = this.doc.sel, doc = this.doc - if (sel.somethingSelected()) - doc.replaceSelection("", null, "+delete") - else - deleteNearSelection(this, range => { - let other = findPosH(doc, range.head, dir, unit, false) - return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} - }) - }), - - findPosV: function(from, amount, unit, goalColumn) { - let dir = 1, x = goalColumn - if (amount < 0) { dir = -1; amount = -amount } - let cur = clipPos(this.doc, from) - for (let i = 0; i < amount; ++i) { - let coords = cursorCoords(this, cur, "div") - if (x == null) x = coords.left - else coords.left = x - cur = findPosV(this, coords, dir, unit) - if (cur.hitSide) break - } - return cur - }, - - moveV: methodOp(function(dir, unit) { - let doc = this.doc, goals = [] - let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected() - doc.extendSelectionsBy(range => { - if (collapse) - return dir < 0 ? range.from() : range.to() - let headPos = cursorCoords(this, range.head, "div") - if (range.goalColumn != null) headPos.left = range.goalColumn - goals.push(headPos.left) - let pos = findPosV(this, headPos, dir, unit) - if (unit == "page" && range == doc.sel.primary()) - addToScrollTop(this, charCoords(this, pos, "div").top - headPos.top) - return pos - }, sel_move) - if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++) - doc.sel.ranges[i].goalColumn = goals[i] - }), - - // Find the word at the given position (as returned by coordsChar). - findWordAt: function(pos) { - let doc = this.doc, line = getLine(doc, pos.line).text - let start = pos.ch, end = pos.ch - if (line) { - let helper = this.getHelper(pos, "wordChars") - if ((pos.sticky == "before" || end == line.length) && start) --start; else ++end - let startChar = line.charAt(start) - let check = isWordChar(startChar, helper) - ? ch => isWordChar(ch, helper) - : /\s/.test(startChar) ? ch => /\s/.test(ch) - : ch => (!/\s/.test(ch) && !isWordChar(ch)) - while (start > 0 && check(line.charAt(start - 1))) --start - while (end < line.length && check(line.charAt(end))) ++end - } - return new Range(Pos(pos.line, start), Pos(pos.line, end)) - }, - - toggleOverwrite: function(value) { - if (value != null && value == this.state.overwrite) return - if (this.state.overwrite = !this.state.overwrite) - addClass(this.display.cursorDiv, "CodeMirror-overwrite") - else - rmClass(this.display.cursorDiv, "CodeMirror-overwrite") - - signal(this, "overwriteToggle", this, this.state.overwrite) - }, - hasFocus: function() { return this.display.input.getField() == activeElt() }, - isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, - - scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y) }), - getScrollInfo: function() { - let scroller = this.display.scroller - return {left: scroller.scrollLeft, top: scroller.scrollTop, - height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, - width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, - clientHeight: displayHeight(this), clientWidth: displayWidth(this)} - }, - - scrollIntoView: methodOp(function(range, margin) { - if (range == null) { - range = {from: this.doc.sel.primary().head, to: null} - if (margin == null) margin = this.options.cursorScrollMargin - } else if (typeof range == "number") { - range = {from: Pos(range, 0), to: null} - } else if (range.from == null) { - range = {from: range, to: null} - } - if (!range.to) range.to = range.from - range.margin = margin || 0 - - if (range.from.line != null) { - scrollToRange(this, range) - } else { - scrollToCoordsRange(this, range.from, range.to, range.margin) - } - }), - - setSize: methodOp(function(width, height) { - let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val - if (width != null) this.display.wrapper.style.width = interpret(width) - if (height != null) this.display.wrapper.style.height = interpret(height) - if (this.options.lineWrapping) clearLineMeasurementCache(this) - let lineNo = this.display.viewFrom - this.doc.iter(lineNo, this.display.viewTo, line => { - if (line.widgets) for (let i = 0; i < line.widgets.length; i++) - if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, "widget"); break } - ++lineNo - }) - this.curOp.forceUpdate = true - signal(this, "refresh", this) - }), - - operation: function(f){return runInOp(this, f)}, - startOperation: function(){return startOperation(this)}, - endOperation: function(){return endOperation(this)}, - - refresh: methodOp(function() { - let oldHeight = this.display.cachedTextHeight - regChange(this) - this.curOp.forceUpdate = true - clearCaches(this) - scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop) - updateGutterSpace(this.display) - if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) - estimateLineHeights(this) - signal(this, "refresh", this) - }), - - swapDoc: methodOp(function(doc) { - let old = this.doc - old.cm = null - // Cancel the current text selection if any (#5821) - if (this.state.selectingText) this.state.selectingText() - attachDoc(this, doc) - clearCaches(this) - this.display.input.reset() - scrollToCoords(this, doc.scrollLeft, doc.scrollTop) - this.curOp.forceScroll = true - signalLater(this, "swapDoc", this, old) - return old - }), - - phrase: function(phraseText) { - let phrases = this.options.phrases - return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText - }, - - getInputField: function(){return this.display.input.getField()}, - getWrapperElement: function(){return this.display.wrapper}, - getScrollerElement: function(){return this.display.scroller}, - getGutterElement: function(){return this.display.gutters} - } - eventMixin(CodeMirror) - - CodeMirror.registerHelper = function(type, name, value) { - if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []} - helpers[type][name] = value - } - CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { - CodeMirror.registerHelper(type, name, value) - helpers[type]._global.push({pred: predicate, val: value}) - } -} - -// Used for horizontal relative motion. Dir is -1 or 1 (left or -// right), unit can be "codepoint", "char", "column" (like char, but -// doesn't cross line boundaries), "word" (across next word), or -// "group" (to the start of next group of word or -// non-word-non-whitespace chars). The visually param controls -// whether, in right-to-left text, direction 1 means to move towards -// the next index in the string, or towards the character to the right -// of the current position. The resulting position will have a -// hitSide=true property if it reached the end of the document. -function findPosH(doc, pos, dir, unit, visually) { - let oldPos = pos - let origDir = dir - let lineObj = getLine(doc, pos.line) - let lineDir = visually && doc.direction == "rtl" ? -dir : dir - function findNextLine() { - let l = pos.line + lineDir - if (l < doc.first || l >= doc.first + doc.size) return false - pos = new Pos(l, pos.ch, pos.sticky) - return lineObj = getLine(doc, l) - } - function moveOnce(boundToLine) { - let next - if (unit == "codepoint") { - let ch = lineObj.text.charCodeAt(pos.ch + (unit > 0 ? 0 : -1)) - if (isNaN(ch)) next = null - else next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (ch >= 0xD800 && ch < 0xDC00 ? 2 : 1))), - -dir) - } else if (visually) { - next = moveVisually(doc.cm, lineObj, pos, dir) - } else { - next = moveLogically(lineObj, pos, dir) - } - if (next == null) { - if (!boundToLine && findNextLine()) - pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir) - else - return false - } else { - pos = next - } - return true - } - - if (unit == "char" || unit == "codepoint") { - moveOnce() - } else if (unit == "column") { - moveOnce(true) - } else if (unit == "word" || unit == "group") { - let sawType = null, group = unit == "group" - let helper = doc.cm && doc.cm.getHelper(pos, "wordChars") - for (let first = true;; first = false) { - if (dir < 0 && !moveOnce(!first)) break - let cur = lineObj.text.charAt(pos.ch) || "\n" - let type = isWordChar(cur, helper) ? "w" - : group && cur == "\n" ? "n" - : !group || /\s/.test(cur) ? null - : "p" - if (group && !first && !type) type = "s" - if (sawType && sawType != type) { - if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"} - break - } - - if (type) sawType = type - if (dir > 0 && !moveOnce(!first)) break - } - } - let result = skipAtomic(doc, pos, oldPos, origDir, true) - if (equalCursorPos(oldPos, result)) result.hitSide = true - return result -} - -// For relative vertical movement. Dir may be -1 or 1. Unit can be -// "page" or "line". The resulting position will have a hitSide=true -// property if it reached the end of the document. -function findPosV(cm, pos, dir, unit) { - let doc = cm.doc, x = pos.left, y - if (unit == "page") { - let pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) - let moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) - y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount - - } else if (unit == "line") { - y = dir > 0 ? pos.bottom + 3 : pos.top - 3 - } - let target - for (;;) { - target = coordsChar(cm, x, y) - if (!target.outside) break - if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } - y += dir * 5 - } - return target -} diff --git a/CODE/js/codemirror/src/edit/mouse_events.js b/CODE/js/codemirror/src/edit/mouse_events.js deleted file mode 100644 index b5d0b5a6..00000000 --- a/CODE/js/codemirror/src/edit/mouse_events.js +++ /dev/null @@ -1,417 +0,0 @@ -import { delayBlurEvent, ensureFocus } from "../display/focus.js" -import { operation } from "../display/operations.js" -import { visibleLines } from "../display/update_lines.js" -import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos.js" -import { getLine, lineAtHeight } from "../line/utils_line.js" -import { posFromMouse } from "../measurement/position_measurement.js" -import { eventInWidget } from "../measurement/widgets.js" -import { normalizeSelection, Range, Selection } from "../model/selection.js" -import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates.js" -import { captureRightClick, chromeOS, ie, ie_version, mac, webkit, safari } from "../util/browser.js" -import { getOrder, getBidiPartAt } from "../util/bidi.js" -import { activeElt } from "../util/dom.js" -import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event.js" -import { dragAndDrop } from "../util/feature_detection.js" -import { bind, countColumn, findColumn, sel_mouse } from "../util/misc.js" -import { addModifierNames } from "../input/keymap.js" -import { Pass } from "../util/misc.js" - -import { dispatchKey } from "./key_events.js" -import { commands } from "./commands.js" - -const DOUBLECLICK_DELAY = 400 - -class PastClick { - constructor(time, pos, button) { - this.time = time - this.pos = pos - this.button = button - } - - compare(time, pos, button) { - return this.time + DOUBLECLICK_DELAY > time && - cmp(pos, this.pos) == 0 && button == this.button - } -} - -let lastClick, lastDoubleClick -function clickRepeat(pos, button) { - let now = +new Date - if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { - lastClick = lastDoubleClick = null - return "triple" - } else if (lastClick && lastClick.compare(now, pos, button)) { - lastDoubleClick = new PastClick(now, pos, button) - lastClick = null - return "double" - } else { - lastClick = new PastClick(now, pos, button) - lastDoubleClick = null - return "single" - } -} - -// A mouse down can be a single click, double click, triple click, -// start of selection drag, start of text drag, new cursor -// (ctrl-click), rectangle drag (alt-drag), or xwin -// middle-click-paste. Or it might be a click on something we should -// not interfere with, such as a scrollbar or widget. -export function onMouseDown(e) { - let cm = this, display = cm.display - if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return - display.input.ensurePolled() - display.shift = e.shiftKey - - if (eventInWidget(display, e)) { - if (!webkit) { - // Briefly turn off draggability, to allow widgets to do - // normal dragging things. - display.scroller.draggable = false - setTimeout(() => display.scroller.draggable = true, 100) - } - return - } - if (clickInGutter(cm, e)) return - let pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single" - window.focus() - - // #3261: make sure, that we're not starting a second selection - if (button == 1 && cm.state.selectingText) - cm.state.selectingText(e) - - if (pos && handleMappedButton(cm, button, pos, repeat, e)) return - - if (button == 1) { - if (pos) leftButtonDown(cm, pos, repeat, e) - else if (e_target(e) == display.scroller) e_preventDefault(e) - } else if (button == 2) { - if (pos) extendSelection(cm.doc, pos) - setTimeout(() => display.input.focus(), 20) - } else if (button == 3) { - if (captureRightClick) cm.display.input.onContextMenu(e) - else delayBlurEvent(cm) - } -} - -function handleMappedButton(cm, button, pos, repeat, event) { - let name = "Click" - if (repeat == "double") name = "Double" + name - else if (repeat == "triple") name = "Triple" + name - name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name - - return dispatchKey(cm, addModifierNames(name, event), event, bound => { - if (typeof bound == "string") bound = commands[bound] - if (!bound) return false - let done = false - try { - if (cm.isReadOnly()) cm.state.suppressEdits = true - done = bound(cm, pos) != Pass - } finally { - cm.state.suppressEdits = false - } - return done - }) -} - -function configureMouse(cm, repeat, event) { - let option = cm.getOption("configureMouse") - let value = option ? option(cm, repeat, event) : {} - if (value.unit == null) { - let rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey - value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line" - } - if (value.extend == null || cm.doc.extend) value.extend = cm.doc.extend || event.shiftKey - if (value.addNew == null) value.addNew = mac ? event.metaKey : event.ctrlKey - if (value.moveOnDrag == null) value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey) - return value -} - -function leftButtonDown(cm, pos, repeat, event) { - if (ie) setTimeout(bind(ensureFocus, cm), 0) - else cm.curOp.focus = activeElt() - - let behavior = configureMouse(cm, repeat, event) - - let sel = cm.doc.sel, contained - if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && - repeat == "single" && (contained = sel.contains(pos)) > -1 && - (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && - (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) - leftButtonStartDrag(cm, event, pos, behavior) - else - leftButtonSelect(cm, event, pos, behavior) -} - -// Start a text drag. When it ends, see if any dragging actually -// happen, and treat as a click if it didn't. -function leftButtonStartDrag(cm, event, pos, behavior) { - let display = cm.display, moved = false - let dragEnd = operation(cm, e => { - if (webkit) display.scroller.draggable = false - cm.state.draggingText = false - if (cm.state.delayingBlurEvent) { - if (cm.hasFocus()) cm.state.delayingBlurEvent = false - else delayBlurEvent(cm) - } - off(display.wrapper.ownerDocument, "mouseup", dragEnd) - off(display.wrapper.ownerDocument, "mousemove", mouseMove) - off(display.scroller, "dragstart", dragStart) - off(display.scroller, "drop", dragEnd) - if (!moved) { - e_preventDefault(e) - if (!behavior.addNew) - extendSelection(cm.doc, pos, null, null, behavior.extend) - // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) - if ((webkit && !safari) || ie && ie_version == 9) - setTimeout(() => {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus()}, 20) - else - display.input.focus() - } - }) - let mouseMove = function(e2) { - moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10 - } - let dragStart = () => moved = true - // Let the drag handler handle this. - if (webkit) display.scroller.draggable = true - cm.state.draggingText = dragEnd - dragEnd.copy = !behavior.moveOnDrag - on(display.wrapper.ownerDocument, "mouseup", dragEnd) - on(display.wrapper.ownerDocument, "mousemove", mouseMove) - on(display.scroller, "dragstart", dragStart) - on(display.scroller, "drop", dragEnd) - - cm.state.delayingBlurEvent = true - setTimeout(() => display.input.focus(), 20) - // IE's approach to draggable - if (display.scroller.dragDrop) display.scroller.dragDrop() -} - -function rangeForUnit(cm, pos, unit) { - if (unit == "char") return new Range(pos, pos) - if (unit == "word") return cm.findWordAt(pos) - if (unit == "line") return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) - let result = unit(cm, pos) - return new Range(result.from, result.to) -} - -// Normal selection, as opposed to text dragging. -function leftButtonSelect(cm, event, start, behavior) { - if (ie) delayBlurEvent(cm) - let display = cm.display, doc = cm.doc - e_preventDefault(event) - - let ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges - if (behavior.addNew && !behavior.extend) { - ourIndex = doc.sel.contains(start) - if (ourIndex > -1) - ourRange = ranges[ourIndex] - else - ourRange = new Range(start, start) - } else { - ourRange = doc.sel.primary() - ourIndex = doc.sel.primIndex - } - - if (behavior.unit == "rectangle") { - if (!behavior.addNew) ourRange = new Range(start, start) - start = posFromMouse(cm, event, true, true) - ourIndex = -1 - } else { - let range = rangeForUnit(cm, start, behavior.unit) - if (behavior.extend) - ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend) - else - ourRange = range - } - - if (!behavior.addNew) { - ourIndex = 0 - setSelection(doc, new Selection([ourRange], 0), sel_mouse) - startSel = doc.sel - } else if (ourIndex == -1) { - ourIndex = ranges.length - setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), - {scroll: false, origin: "*mouse"}) - } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { - setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), - {scroll: false, origin: "*mouse"}) - startSel = doc.sel - } else { - replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) - } - - let lastPos = start - function extendTo(pos) { - if (cmp(lastPos, pos) == 0) return - lastPos = pos - - if (behavior.unit == "rectangle") { - let ranges = [], tabSize = cm.options.tabSize - let startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) - let posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) - let left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) - for (let line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); - line <= end; line++) { - let text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) - if (left == right) - ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) - else if (text.length > leftPos) - ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) - } - if (!ranges.length) ranges.push(new Range(start, start)) - setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), - {origin: "*mouse", scroll: false}) - cm.scrollIntoView(pos) - } else { - let oldRange = ourRange - let range = rangeForUnit(cm, pos, behavior.unit) - let anchor = oldRange.anchor, head - if (cmp(range.anchor, anchor) > 0) { - head = range.head - anchor = minPos(oldRange.from(), range.anchor) - } else { - head = range.anchor - anchor = maxPos(oldRange.to(), range.head) - } - let ranges = startSel.ranges.slice(0) - ranges[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)) - setSelection(doc, normalizeSelection(cm, ranges, ourIndex), sel_mouse) - } - } - - let editorSize = display.wrapper.getBoundingClientRect() - // Used to ensure timeout re-tries don't fire when another extend - // happened in the meantime (clearTimeout isn't reliable -- at - // least on Chrome, the timeouts still happen even when cleared, - // if the clear happens after their scheduled firing time). - let counter = 0 - - function extend(e) { - let curCount = ++counter - let cur = posFromMouse(cm, e, true, behavior.unit == "rectangle") - if (!cur) return - if (cmp(cur, lastPos) != 0) { - cm.curOp.focus = activeElt() - extendTo(cur) - let visible = visibleLines(display, doc) - if (cur.line >= visible.to || cur.line < visible.from) - setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150) - } else { - let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 - if (outside) setTimeout(operation(cm, () => { - if (counter != curCount) return - display.scroller.scrollTop += outside - extend(e) - }), 50) - } - } - - function done(e) { - cm.state.selectingText = false - counter = Infinity - // If e is null or undefined we interpret this as someone trying - // to explicitly cancel the selection rather than the user - // letting go of the mouse button. - if (e) { - e_preventDefault(e) - display.input.focus() - } - off(display.wrapper.ownerDocument, "mousemove", move) - off(display.wrapper.ownerDocument, "mouseup", up) - doc.history.lastSelOrigin = null - } - - let move = operation(cm, e => { - if (e.buttons === 0 || !e_button(e)) done(e) - else extend(e) - }) - let up = operation(cm, done) - cm.state.selectingText = up - on(display.wrapper.ownerDocument, "mousemove", move) - on(display.wrapper.ownerDocument, "mouseup", up) -} - -// Used when mouse-selecting to adjust the anchor to the proper side -// of a bidi jump depending on the visual position of the head. -function bidiSimplify(cm, range) { - let {anchor, head} = range, anchorLine = getLine(cm.doc, anchor.line) - if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) return range - let order = getOrder(anchorLine) - if (!order) return range - let index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index] - if (part.from != anchor.ch && part.to != anchor.ch) return range - let boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1) - if (boundary == 0 || boundary == order.length) return range - - // Compute the relative visual position of the head compared to the - // anchor (<0 is to the left, >0 to the right) - let leftSide - if (head.line != anchor.line) { - leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0 - } else { - let headIndex = getBidiPartAt(order, head.ch, head.sticky) - let dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1) - if (headIndex == boundary - 1 || headIndex == boundary) - leftSide = dir < 0 - else - leftSide = dir > 0 - } - - let usePart = order[boundary + (leftSide ? -1 : 0)] - let from = leftSide == (usePart.level == 1) - let ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before" - return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) -} - - -// Determines whether an event happened in the gutter, and fires the -// handlers for the corresponding event. -function gutterEvent(cm, e, type, prevent) { - let mX, mY - if (e.touches) { - mX = e.touches[0].clientX - mY = e.touches[0].clientY - } else { - try { mX = e.clientX; mY = e.clientY } - catch(e) { return false } - } - if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false - if (prevent) e_preventDefault(e) - - let display = cm.display - let lineBox = display.lineDiv.getBoundingClientRect() - - if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) - mY -= lineBox.top - display.viewOffset - - for (let i = 0; i < cm.display.gutterSpecs.length; ++i) { - let g = display.gutters.childNodes[i] - if (g && g.getBoundingClientRect().right >= mX) { - let line = lineAtHeight(cm.doc, mY) - let gutter = cm.display.gutterSpecs[i] - signal(cm, type, cm, line, gutter.className, e) - return e_defaultPrevented(e) - } - } -} - -export function clickInGutter(cm, e) { - return gutterEvent(cm, e, "gutterClick", true) -} - -// CONTEXT MENU HANDLING - -// To make the context menu work, we need to briefly unhide the -// textarea (making it as unobtrusive as possible) to let the -// right-click take effect on it. -export function onContextMenu(cm, e) { - if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return - if (signalDOMEvent(cm, e, "contextmenu")) return - if (!captureRightClick) cm.display.input.onContextMenu(e) -} - -function contextMenuInGutter(cm, e) { - if (!hasHandler(cm, "gutterContextMenu")) return false - return gutterEvent(cm, e, "gutterContextMenu", false) -} diff --git a/CODE/js/codemirror/src/edit/options.js b/CODE/js/codemirror/src/edit/options.js deleted file mode 100644 index 400e0003..00000000 --- a/CODE/js/codemirror/src/edit/options.js +++ /dev/null @@ -1,194 +0,0 @@ -import { onBlur } from "../display/focus.js" -import { getGutters, updateGutters } from "../display/gutters.js" -import { loadMode, resetModeState } from "../display/mode_state.js" -import { initScrollbars, updateScrollbars } from "../display/scrollbars.js" -import { updateSelection } from "../display/selection.js" -import { regChange } from "../display/view_tracking.js" -import { getKeyMap } from "../input/keymap.js" -import { defaultSpecialCharPlaceholder } from "../line/line_data.js" -import { Pos } from "../line/pos.js" -import { findMaxLine } from "../line/spans.js" -import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement.js" -import { replaceRange } from "../model/changes.js" -import { mobile, windows } from "../util/browser.js" -import { addClass, rmClass } from "../util/dom.js" -import { off, on } from "../util/event.js" - -import { themeChanged } from "./utils.js" - -export let Init = {toString: function(){return "CodeMirror.Init"}} - -export let defaults = {} -export let optionHandlers = {} - -export function defineOptions(CodeMirror) { - let optionHandlers = CodeMirror.optionHandlers - - function option(name, deflt, handle, notOnInit) { - CodeMirror.defaults[name] = deflt - if (handle) optionHandlers[name] = - notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle - } - - CodeMirror.defineOption = option - - // Passed to option handlers when there is no old value. - CodeMirror.Init = Init - - // These two are, on init, called from the constructor because they - // have to be initialized before the editor can start at all. - option("value", "", (cm, val) => cm.setValue(val), true) - option("mode", null, (cm, val) => { - cm.doc.modeOption = val - loadMode(cm) - }, true) - - option("indentUnit", 2, loadMode, true) - option("indentWithTabs", false) - option("smartIndent", true) - option("tabSize", 4, cm => { - resetModeState(cm) - clearCaches(cm) - regChange(cm) - }, true) - - option("lineSeparator", null, (cm, val) => { - cm.doc.lineSep = val - if (!val) return - let newBreaks = [], lineNo = cm.doc.first - cm.doc.iter(line => { - for (let pos = 0;;) { - let found = line.text.indexOf(val, pos) - if (found == -1) break - pos = found + val.length - newBreaks.push(Pos(lineNo, found)) - } - lineNo++ - }) - for (let i = newBreaks.length - 1; i >= 0; i--) - replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) - }) - option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, (cm, val, old) => { - cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") - if (old != Init) cm.refresh() - }) - option("specialCharPlaceholder", defaultSpecialCharPlaceholder, cm => cm.refresh(), true) - option("electricChars", true) - option("inputStyle", mobile ? "contenteditable" : "textarea", () => { - throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME - }, true) - option("spellcheck", false, (cm, val) => cm.getInputField().spellcheck = val, true) - option("autocorrect", false, (cm, val) => cm.getInputField().autocorrect = val, true) - option("autocapitalize", false, (cm, val) => cm.getInputField().autocapitalize = val, true) - option("rtlMoveVisually", !windows) - option("wholeLineUpdateBefore", true) - - option("theme", "default", cm => { - themeChanged(cm) - updateGutters(cm) - }, true) - option("keyMap", "default", (cm, val, old) => { - let next = getKeyMap(val) - let prev = old != Init && getKeyMap(old) - if (prev && prev.detach) prev.detach(cm, next) - if (next.attach) next.attach(cm, prev || null) - }) - option("extraKeys", null) - option("configureMouse", null) - - option("lineWrapping", false, wrappingChanged, true) - option("gutters", [], (cm, val) => { - cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers) - updateGutters(cm) - }, true) - option("fixedGutter", true, (cm, val) => { - cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" - cm.refresh() - }, true) - option("coverGutterNextToScrollbar", false, cm => updateScrollbars(cm), true) - option("scrollbarStyle", "native", cm => { - initScrollbars(cm) - updateScrollbars(cm) - cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) - cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) - }, true) - option("lineNumbers", false, (cm, val) => { - cm.display.gutterSpecs = getGutters(cm.options.gutters, val) - updateGutters(cm) - }, true) - option("firstLineNumber", 1, updateGutters, true) - option("lineNumberFormatter", integer => integer, updateGutters, true) - option("showCursorWhenSelecting", false, updateSelection, true) - - option("resetSelectionOnContextMenu", true) - option("lineWiseCopyCut", true) - option("pasteLinesPerSelection", true) - option("selectionsMayTouch", false) - - option("readOnly", false, (cm, val) => { - if (val == "nocursor") { - onBlur(cm) - cm.display.input.blur() - } - cm.display.input.readOnlyChanged(val) - }) - - option("screenReaderLabel", null, (cm, val) => { - val = (val === '') ? null : val - cm.display.input.screenReaderLabelChanged(val) - }) - - option("disableInput", false, (cm, val) => {if (!val) cm.display.input.reset()}, true) - option("dragDrop", true, dragDropChanged) - option("allowDropFileTypes", null) - - option("cursorBlinkRate", 530) - option("cursorScrollMargin", 0) - option("cursorHeight", 1, updateSelection, true) - option("singleCursorHeightPerLine", true, updateSelection, true) - option("workTime", 100) - option("workDelay", 100) - option("flattenSpans", true, resetModeState, true) - option("addModeClass", false, resetModeState, true) - option("pollInterval", 100) - option("undoDepth", 200, (cm, val) => cm.doc.history.undoDepth = val) - option("historyEventDelay", 1250) - option("viewportMargin", 10, cm => cm.refresh(), true) - option("maxHighlightLength", 10000, resetModeState, true) - option("moveInputWithCursor", true, (cm, val) => { - if (!val) cm.display.input.resetPosition() - }) - - option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "") - option("autofocus", null) - option("direction", "ltr", (cm, val) => cm.doc.setDirection(val), true) - option("phrases", null) -} - -function dragDropChanged(cm, value, old) { - let wasOn = old && old != Init - if (!value != !wasOn) { - let funcs = cm.display.dragFunctions - let toggle = value ? on : off - toggle(cm.display.scroller, "dragstart", funcs.start) - toggle(cm.display.scroller, "dragenter", funcs.enter) - toggle(cm.display.scroller, "dragover", funcs.over) - toggle(cm.display.scroller, "dragleave", funcs.leave) - toggle(cm.display.scroller, "drop", funcs.drop) - } -} - -function wrappingChanged(cm) { - if (cm.options.lineWrapping) { - addClass(cm.display.wrapper, "CodeMirror-wrap") - cm.display.sizer.style.minWidth = "" - cm.display.sizerWidth = null - } else { - rmClass(cm.display.wrapper, "CodeMirror-wrap") - findMaxLine(cm) - } - estimateLineHeights(cm) - regChange(cm) - clearCaches(cm) - setTimeout(() => updateScrollbars(cm), 100) -} diff --git a/CODE/js/codemirror/src/edit/utils.js b/CODE/js/codemirror/src/edit/utils.js deleted file mode 100644 index fda0be74..00000000 --- a/CODE/js/codemirror/src/edit/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -import { clearCaches } from "../measurement/position_measurement.js" - -export function themeChanged(cm) { - cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + - cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-") - clearCaches(cm) -} diff --git a/CODE/js/codemirror/src/input/ContentEditableInput.js b/CODE/js/codemirror/src/input/ContentEditableInput.js deleted file mode 100644 index b50073ec..00000000 --- a/CODE/js/codemirror/src/input/ContentEditableInput.js +++ /dev/null @@ -1,544 +0,0 @@ -import { operation, runInOp } from "../display/operations.js" -import { prepareSelection } from "../display/selection.js" -import { regChange } from "../display/view_tracking.js" -import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input.js" -import { cmp, maxPos, minPos, Pos } from "../line/pos.js" -import { getBetween, getLine, lineNo } from "../line/utils_line.js" -import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement.js" -import { replaceRange } from "../model/changes.js" -import { simpleSelection } from "../model/selection.js" -import { setSelection } from "../model/selection_updates.js" -import { getBidiPartAt, getOrder } from "../util/bidi.js" -import { android, chrome, gecko, ie_version } from "../util/browser.js" -import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom.js" -import { on, signalDOMEvent } from "../util/event.js" -import { Delayed, lst, sel_dontScroll } from "../util/misc.js" - -// CONTENTEDITABLE INPUT STYLE - -export default class ContentEditableInput { - constructor(cm) { - this.cm = cm - this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null - this.polling = new Delayed() - this.composing = null - this.gracePeriod = false - this.readDOMTimeout = null - } - - init(display) { - let input = this, cm = input.cm - let div = input.div = display.lineDiv - disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize) - - function belongsToInput(e) { - for (let t = e.target; t; t = t.parentNode) { - if (t == div) return true - if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) break - } - return false - } - - on(div, "paste", e => { - if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) return - // IE doesn't fire input events, so we schedule a read for the pasted content in this way - if (ie_version <= 11) setTimeout(operation(cm, () => this.updateFromDOM()), 20) - }) - - on(div, "compositionstart", e => { - this.composing = {data: e.data, done: false} - }) - on(div, "compositionupdate", e => { - if (!this.composing) this.composing = {data: e.data, done: false} - }) - on(div, "compositionend", e => { - if (this.composing) { - if (e.data != this.composing.data) this.readFromDOMSoon() - this.composing.done = true - } - }) - - on(div, "touchstart", () => input.forceCompositionEnd()) - - on(div, "input", () => { - if (!this.composing) this.readFromDOMSoon() - }) - - function onCopyCut(e) { - if (!belongsToInput(e) || signalDOMEvent(cm, e)) return - if (cm.somethingSelected()) { - setLastCopied({lineWise: false, text: cm.getSelections()}) - if (e.type == "cut") cm.replaceSelection("", null, "cut") - } else if (!cm.options.lineWiseCopyCut) { - return - } else { - let ranges = copyableRanges(cm) - setLastCopied({lineWise: true, text: ranges.text}) - if (e.type == "cut") { - cm.operation(() => { - cm.setSelections(ranges.ranges, 0, sel_dontScroll) - cm.replaceSelection("", null, "cut") - }) - } - } - if (e.clipboardData) { - e.clipboardData.clearData() - let content = lastCopied.text.join("\n") - // iOS exposes the clipboard API, but seems to discard content inserted into it - e.clipboardData.setData("Text", content) - if (e.clipboardData.getData("Text") == content) { - e.preventDefault() - return - } - } - // Old-fashioned briefly-focus-a-textarea hack - let kludge = hiddenTextarea(), te = kludge.firstChild - cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild) - te.value = lastCopied.text.join("\n") - let hadFocus = document.activeElement - selectInput(te) - setTimeout(() => { - cm.display.lineSpace.removeChild(kludge) - hadFocus.focus() - if (hadFocus == div) input.showPrimarySelection() - }, 50) - } - on(div, "copy", onCopyCut) - on(div, "cut", onCopyCut) - } - - screenReaderLabelChanged(label) { - // Label for screenreaders, accessibility - if(label) { - this.div.setAttribute('aria-label', label) - } else { - this.div.removeAttribute('aria-label') - } - } - - prepareSelection() { - let result = prepareSelection(this.cm, false) - result.focus = document.activeElement == this.div - return result - } - - showSelection(info, takeFocus) { - if (!info || !this.cm.display.view.length) return - if (info.focus || takeFocus) this.showPrimarySelection() - this.showMultipleSelections(info) - } - - getSelection() { - return this.cm.display.wrapper.ownerDocument.getSelection() - } - - showPrimarySelection() { - let sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary() - let from = prim.from(), to = prim.to() - - if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { - sel.removeAllRanges() - return - } - - let curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) - let curFocus = domToPos(cm, sel.focusNode, sel.focusOffset) - if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && - cmp(minPos(curAnchor, curFocus), from) == 0 && - cmp(maxPos(curAnchor, curFocus), to) == 0) - return - - let view = cm.display.view - let start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || - {node: view[0].measure.map[2], offset: 0} - let end = to.line < cm.display.viewTo && posToDOM(cm, to) - if (!end) { - let measure = view[view.length - 1].measure - let map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map - end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]} - } - - if (!start || !end) { - sel.removeAllRanges() - return - } - - let old = sel.rangeCount && sel.getRangeAt(0), rng - try { rng = range(start.node, start.offset, end.offset, end.node) } - catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible - if (rng) { - if (!gecko && cm.state.focused) { - sel.collapse(start.node, start.offset) - if (!rng.collapsed) { - sel.removeAllRanges() - sel.addRange(rng) - } - } else { - sel.removeAllRanges() - sel.addRange(rng) - } - if (old && sel.anchorNode == null) sel.addRange(old) - else if (gecko) this.startGracePeriod() - } - this.rememberSelection() - } - - startGracePeriod() { - clearTimeout(this.gracePeriod) - this.gracePeriod = setTimeout(() => { - this.gracePeriod = false - if (this.selectionChanged()) - this.cm.operation(() => this.cm.curOp.selectionChanged = true) - }, 20) - } - - showMultipleSelections(info) { - removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors) - removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection) - } - - rememberSelection() { - let sel = this.getSelection() - this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset - this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset - } - - selectionInEditor() { - let sel = this.getSelection() - if (!sel.rangeCount) return false - let node = sel.getRangeAt(0).commonAncestorContainer - return contains(this.div, node) - } - - focus() { - if (this.cm.options.readOnly != "nocursor") { - if (!this.selectionInEditor() || document.activeElement != this.div) - this.showSelection(this.prepareSelection(), true) - this.div.focus() - } - } - blur() { this.div.blur() } - getField() { return this.div } - - supportsTouch() { return true } - - receivedFocus() { - let input = this - if (this.selectionInEditor()) - this.pollSelection() - else - runInOp(this.cm, () => input.cm.curOp.selectionChanged = true) - - function poll() { - if (input.cm.state.focused) { - input.pollSelection() - input.polling.set(input.cm.options.pollInterval, poll) - } - } - this.polling.set(this.cm.options.pollInterval, poll) - } - - selectionChanged() { - let sel = this.getSelection() - return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || - sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset - } - - pollSelection() { - if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) return - let sel = this.getSelection(), cm = this.cm - // On Android Chrome (version 56, at least), backspacing into an - // uneditable block element will put the cursor in that element, - // and then, because it's not editable, hide the virtual keyboard. - // Because Android doesn't allow us to actually detect backspace - // presses in a sane way, this code checks for when that happens - // and simulates a backspace press in this case. - if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { - this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}) - this.blur() - this.focus() - return - } - if (this.composing) return - this.rememberSelection() - let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) - let head = domToPos(cm, sel.focusNode, sel.focusOffset) - if (anchor && head) runInOp(cm, () => { - setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) - if (anchor.bad || head.bad) cm.curOp.selectionChanged = true - }) - } - - pollContent() { - if (this.readDOMTimeout != null) { - clearTimeout(this.readDOMTimeout) - this.readDOMTimeout = null - } - - let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() - let from = sel.from(), to = sel.to() - if (from.ch == 0 && from.line > cm.firstLine()) - from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) - if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) - to = Pos(to.line + 1, 0) - if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false - - let fromIndex, fromLine, fromNode - if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { - fromLine = lineNo(display.view[0].line) - fromNode = display.view[0].node - } else { - fromLine = lineNo(display.view[fromIndex].line) - fromNode = display.view[fromIndex - 1].node.nextSibling - } - let toIndex = findViewIndex(cm, to.line) - let toLine, toNode - if (toIndex == display.view.length - 1) { - toLine = display.viewTo - 1 - toNode = display.lineDiv.lastChild - } else { - toLine = lineNo(display.view[toIndex + 1].line) - 1 - toNode = display.view[toIndex + 1].node.previousSibling - } - - if (!fromNode) return false - let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) - let oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) - while (newText.length > 1 && oldText.length > 1) { - if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- } - else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ } - else break - } - - let cutFront = 0, cutEnd = 0 - let newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) - while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) - ++cutFront - let newBot = lst(newText), oldBot = lst(oldText) - let maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), - oldBot.length - (oldText.length == 1 ? cutFront : 0)) - while (cutEnd < maxCutEnd && - newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) - ++cutEnd - // Try to move start of change to start of selection if ambiguous - if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { - while (cutFront && cutFront > from.ch && - newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { - cutFront-- - cutEnd++ - } - } - - newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "") - newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "") - - let chFrom = Pos(fromLine, cutFront) - let chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) - if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { - replaceRange(cm.doc, newText, chFrom, chTo, "+input") - return true - } - } - - ensurePolled() { - this.forceCompositionEnd() - } - reset() { - this.forceCompositionEnd() - } - forceCompositionEnd() { - if (!this.composing) return - clearTimeout(this.readDOMTimeout) - this.composing = null - this.updateFromDOM() - this.div.blur() - this.div.focus() - } - readFromDOMSoon() { - if (this.readDOMTimeout != null) return - this.readDOMTimeout = setTimeout(() => { - this.readDOMTimeout = null - if (this.composing) { - if (this.composing.done) this.composing = null - else return - } - this.updateFromDOM() - }, 80) - } - - updateFromDOM() { - if (this.cm.isReadOnly() || !this.pollContent()) - runInOp(this.cm, () => regChange(this.cm)) - } - - setUneditable(node) { - node.contentEditable = "false" - } - - onKeyPress(e) { - if (e.charCode == 0 || this.composing) return - e.preventDefault() - if (!this.cm.isReadOnly()) - operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) - } - - readOnlyChanged(val) { - this.div.contentEditable = String(val != "nocursor") - } - - onContextMenu() {} - resetPosition() {} -} - -ContentEditableInput.prototype.needsContentAttribute = true - -function posToDOM(cm, pos) { - let view = findViewForLine(cm, pos.line) - if (!view || view.hidden) return null - let line = getLine(cm.doc, pos.line) - let info = mapFromLineView(view, line, pos.line) - - let order = getOrder(line, cm.doc.direction), side = "left" - if (order) { - let partPos = getBidiPartAt(order, pos.ch) - side = partPos % 2 ? "right" : "left" - } - let result = nodeAndOffsetInLineMap(info.map, pos.ch, side) - result.offset = result.collapse == "right" ? result.end : result.start - return result -} - -function isInGutter(node) { - for (let scan = node; scan; scan = scan.parentNode) - if (/CodeMirror-gutter-wrapper/.test(scan.className)) return true - return false -} - -function badPos(pos, bad) { if (bad) pos.bad = true; return pos } - -function domTextBetween(cm, from, to, fromLine, toLine) { - let text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false - function recognizeMarker(id) { return marker => marker.id == id } - function close() { - if (closing) { - text += lineSep - if (extraLinebreak) text += lineSep - closing = extraLinebreak = false - } - } - function addText(str) { - if (str) { - close() - text += str - } - } - function walk(node) { - if (node.nodeType == 1) { - let cmText = node.getAttribute("cm-text") - if (cmText) { - addText(cmText) - return - } - let markerID = node.getAttribute("cm-marker"), range - if (markerID) { - let found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) - if (found.length && (range = found[0].find(0))) - addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) - return - } - if (node.getAttribute("contenteditable") == "false") return - let isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName) - if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) return - - if (isBlock) close() - for (let i = 0; i < node.childNodes.length; i++) - walk(node.childNodes[i]) - - if (/^(pre|p)$/i.test(node.nodeName)) extraLinebreak = true - if (isBlock) closing = true - } else if (node.nodeType == 3) { - addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")) - } - } - for (;;) { - walk(from) - if (from == to) break - from = from.nextSibling - extraLinebreak = false - } - return text -} - -function domToPos(cm, node, offset) { - let lineNode - if (node == cm.display.lineDiv) { - lineNode = cm.display.lineDiv.childNodes[offset] - if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) - node = null; offset = 0 - } else { - for (lineNode = node;; lineNode = lineNode.parentNode) { - if (!lineNode || lineNode == cm.display.lineDiv) return null - if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break - } - } - for (let i = 0; i < cm.display.view.length; i++) { - let lineView = cm.display.view[i] - if (lineView.node == lineNode) - return locateNodeInLineView(lineView, node, offset) - } -} - -function locateNodeInLineView(lineView, node, offset) { - let wrapper = lineView.text.firstChild, bad = false - if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true) - if (node == wrapper) { - bad = true - node = wrapper.childNodes[offset] - offset = 0 - if (!node) { - let line = lineView.rest ? lst(lineView.rest) : lineView.line - return badPos(Pos(lineNo(line), line.text.length), bad) - } - } - - let textNode = node.nodeType == 3 ? node : null, topNode = node - if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { - textNode = node.firstChild - if (offset) offset = textNode.nodeValue.length - } - while (topNode.parentNode != wrapper) topNode = topNode.parentNode - let measure = lineView.measure, maps = measure.maps - - function find(textNode, topNode, offset) { - for (let i = -1; i < (maps ? maps.length : 0); i++) { - let map = i < 0 ? measure.map : maps[i] - for (let j = 0; j < map.length; j += 3) { - let curNode = map[j + 2] - if (curNode == textNode || curNode == topNode) { - let line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) - let ch = map[j] + offset - if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)] - return Pos(line, ch) - } - } - } - } - let found = find(textNode, topNode, offset) - if (found) return badPos(found, bad) - - // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems - for (let after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { - found = find(after, after.firstChild, 0) - if (found) - return badPos(Pos(found.line, found.ch - dist), bad) - else - dist += after.textContent.length - } - for (let before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { - found = find(before, before.firstChild, -1) - if (found) - return badPos(Pos(found.line, found.ch + dist), bad) - else - dist += before.textContent.length - } -} diff --git a/CODE/js/codemirror/src/input/TextareaInput.js b/CODE/js/codemirror/src/input/TextareaInput.js deleted file mode 100644 index 977eb227..00000000 --- a/CODE/js/codemirror/src/input/TextareaInput.js +++ /dev/null @@ -1,375 +0,0 @@ -import { operation, runInOp } from "../display/operations.js" -import { prepareSelection } from "../display/selection.js" -import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied } from "./input.js" -import { cursorCoords, posFromMouse } from "../measurement/position_measurement.js" -import { eventInWidget } from "../measurement/widgets.js" -import { simpleSelection } from "../model/selection.js" -import { selectAll, setSelection } from "../model/selection_updates.js" -import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser.js" -import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom.js" -import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event.js" -import { hasSelection } from "../util/feature_detection.js" -import { Delayed, sel_dontScroll } from "../util/misc.js" - -// TEXTAREA INPUT STYLE - -export default class TextareaInput { - constructor(cm) { - this.cm = cm - // See input.poll and input.reset - this.prevInput = "" - - // Flag that indicates whether we expect input to appear real soon - // now (after some event like 'keypress' or 'input') and are - // polling intensively. - this.pollingFast = false - // Self-resetting timeout for the poller - this.polling = new Delayed() - // Used to work around IE issue with selection being forgotten when focus moves away from textarea - this.hasSelection = false - this.composing = null - } - - init(display) { - let input = this, cm = this.cm - this.createField(display) - const te = this.textarea - - display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild) - - // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) - if (ios) te.style.width = "0px" - - on(te, "input", () => { - if (ie && ie_version >= 9 && this.hasSelection) this.hasSelection = null - input.poll() - }) - - on(te, "paste", e => { - if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return - - cm.state.pasteIncoming = +new Date - input.fastPoll() - }) - - function prepareCopyCut(e) { - if (signalDOMEvent(cm, e)) return - if (cm.somethingSelected()) { - setLastCopied({lineWise: false, text: cm.getSelections()}) - } else if (!cm.options.lineWiseCopyCut) { - return - } else { - let ranges = copyableRanges(cm) - setLastCopied({lineWise: true, text: ranges.text}) - if (e.type == "cut") { - cm.setSelections(ranges.ranges, null, sel_dontScroll) - } else { - input.prevInput = "" - te.value = ranges.text.join("\n") - selectInput(te) - } - } - if (e.type == "cut") cm.state.cutIncoming = +new Date - } - on(te, "cut", prepareCopyCut) - on(te, "copy", prepareCopyCut) - - on(display.scroller, "paste", e => { - if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return - if (!te.dispatchEvent) { - cm.state.pasteIncoming = +new Date - input.focus() - return - } - - // Pass the `paste` event to the textarea so it's handled by its event listener. - const event = new Event("paste") - event.clipboardData = e.clipboardData - te.dispatchEvent(event) - }) - - // Prevent normal selection in the editor (we handle our own) - on(display.lineSpace, "selectstart", e => { - if (!eventInWidget(display, e)) e_preventDefault(e) - }) - - on(te, "compositionstart", () => { - let start = cm.getCursor("from") - if (input.composing) input.composing.range.clear() - input.composing = { - start: start, - range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) - } - }) - on(te, "compositionend", () => { - if (input.composing) { - input.poll() - input.composing.range.clear() - input.composing = null - } - }) - } - - createField(_display) { - // Wraps and hides input textarea - this.wrapper = hiddenTextarea() - // The semihidden textarea that is focused when the editor is - // focused, and receives input. - this.textarea = this.wrapper.firstChild - } - - screenReaderLabelChanged(label) { - // Label for screenreaders, accessibility - if(label) { - this.textarea.setAttribute('aria-label', label) - } else { - this.textarea.removeAttribute('aria-label') - } - } - - prepareSelection() { - // Redraw the selection and/or cursor - let cm = this.cm, display = cm.display, doc = cm.doc - let result = prepareSelection(cm) - - // Move the hidden textarea near the cursor to prevent scrolling artifacts - if (cm.options.moveInputWithCursor) { - let headPos = cursorCoords(cm, doc.sel.primary().head, "div") - let wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect() - result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, - headPos.top + lineOff.top - wrapOff.top)) - result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, - headPos.left + lineOff.left - wrapOff.left)) - } - - return result - } - - showSelection(drawn) { - let cm = this.cm, display = cm.display - removeChildrenAndAdd(display.cursorDiv, drawn.cursors) - removeChildrenAndAdd(display.selectionDiv, drawn.selection) - if (drawn.teTop != null) { - this.wrapper.style.top = drawn.teTop + "px" - this.wrapper.style.left = drawn.teLeft + "px" - } - } - - // Reset the input to correspond to the selection (or to be empty, - // when not typing and nothing is selected) - reset(typing) { - if (this.contextMenuPending || this.composing) return - let cm = this.cm - if (cm.somethingSelected()) { - this.prevInput = "" - let content = cm.getSelection() - this.textarea.value = content - if (cm.state.focused) selectInput(this.textarea) - if (ie && ie_version >= 9) this.hasSelection = content - } else if (!typing) { - this.prevInput = this.textarea.value = "" - if (ie && ie_version >= 9) this.hasSelection = null - } - } - - getField() { return this.textarea } - - supportsTouch() { return false } - - focus() { - if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { - try { this.textarea.focus() } - catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM - } - } - - blur() { this.textarea.blur() } - - resetPosition() { - this.wrapper.style.top = this.wrapper.style.left = 0 - } - - receivedFocus() { this.slowPoll() } - - // Poll for input changes, using the normal rate of polling. This - // runs as long as the editor is focused. - slowPoll() { - if (this.pollingFast) return - this.polling.set(this.cm.options.pollInterval, () => { - this.poll() - if (this.cm.state.focused) this.slowPoll() - }) - } - - // When an event has just come in that is likely to add or change - // something in the input textarea, we poll faster, to ensure that - // the change appears on the screen quickly. - fastPoll() { - let missed = false, input = this - input.pollingFast = true - function p() { - let changed = input.poll() - if (!changed && !missed) {missed = true; input.polling.set(60, p)} - else {input.pollingFast = false; input.slowPoll()} - } - input.polling.set(20, p) - } - - // Read input from the textarea, and update the document to match. - // When something is selected, it is present in the textarea, and - // selected (unless it is huge, in which case a placeholder is - // used). When nothing is selected, the cursor sits after previously - // seen text (can be empty), which is stored in prevInput (we must - // not reset the textarea when typing, because that breaks IME). - poll() { - let cm = this.cm, input = this.textarea, prevInput = this.prevInput - // Since this is called a *lot*, try to bail out as cheaply as - // possible when it is clear that nothing happened. hasSelection - // will be the case when there is a lot of text in the textarea, - // in which case reading its value would be expensive. - if (this.contextMenuPending || !cm.state.focused || - (hasSelection(input) && !prevInput && !this.composing) || - cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) - return false - - let text = input.value - // If nothing changed, bail. - if (text == prevInput && !cm.somethingSelected()) return false - // Work around nonsensical selection resetting in IE9/10, and - // inexplicable appearance of private area unicode characters on - // some key combos in Mac (#2689). - if (ie && ie_version >= 9 && this.hasSelection === text || - mac && /[\uf700-\uf7ff]/.test(text)) { - cm.display.input.reset() - return false - } - - if (cm.doc.sel == cm.display.selForContextMenu) { - let first = text.charCodeAt(0) - if (first == 0x200b && !prevInput) prevInput = "\u200b" - if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } - } - // Find the part of the input that is actually new - let same = 0, l = Math.min(prevInput.length, text.length) - while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same - - runInOp(cm, () => { - applyTextInput(cm, text.slice(same), prevInput.length - same, - null, this.composing ? "*compose" : null) - - // Don't leave long text in the textarea, since it makes further polling slow - if (text.length > 1000 || text.indexOf("\n") > -1) input.value = this.prevInput = "" - else this.prevInput = text - - if (this.composing) { - this.composing.range.clear() - this.composing.range = cm.markText(this.composing.start, cm.getCursor("to"), - {className: "CodeMirror-composing"}) - } - }) - return true - } - - ensurePolled() { - if (this.pollingFast && this.poll()) this.pollingFast = false - } - - onKeyPress() { - if (ie && ie_version >= 9) this.hasSelection = null - this.fastPoll() - } - - onContextMenu(e) { - let input = this, cm = input.cm, display = cm.display, te = input.textarea - if (input.contextMenuPending) input.contextMenuPending() - let pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop - if (!pos || presto) return // Opera is difficult. - - // Reset the current text selection only if the click is done outside of the selection - // and 'resetSelectionOnContextMenu' option is true. - let reset = cm.options.resetSelectionOnContextMenu - if (reset && cm.doc.sel.contains(pos) == -1) - operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) - - let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText - let wrapperBox = input.wrapper.offsetParent.getBoundingClientRect() - input.wrapper.style.cssText = "position: static" - te.style.cssText = `position: absolute; width: 30px; height: 30px; - top: ${e.clientY - wrapperBox.top - 5}px; left: ${e.clientX - wrapperBox.left - 5}px; - z-index: 1000; background: ${ie ? "rgba(255, 255, 255, .05)" : "transparent"}; - outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);` - let oldScrollY - if (webkit) oldScrollY = window.scrollY // Work around Chrome issue (#2712) - display.input.focus() - if (webkit) window.scrollTo(null, oldScrollY) - display.input.reset() - // Adds "Select all" to context menu in FF - if (!cm.somethingSelected()) te.value = input.prevInput = " " - input.contextMenuPending = rehide - display.selForContextMenu = cm.doc.sel - clearTimeout(display.detectingSelectAll) - - // Select-all will be greyed out if there's nothing to select, so - // this adds a zero-width space so that we can later check whether - // it got selected. - function prepareSelectAllHack() { - if (te.selectionStart != null) { - let selected = cm.somethingSelected() - let extval = "\u200b" + (selected ? te.value : "") - te.value = "\u21da" // Used to catch context-menu undo - te.value = extval - input.prevInput = selected ? "" : "\u200b" - te.selectionStart = 1; te.selectionEnd = extval.length - // Re-set this, in case some other handler touched the - // selection in the meantime. - display.selForContextMenu = cm.doc.sel - } - } - function rehide() { - if (input.contextMenuPending != rehide) return - input.contextMenuPending = false - input.wrapper.style.cssText = oldWrapperCSS - te.style.cssText = oldCSS - if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) - - // Try to detect the user choosing select-all - if (te.selectionStart != null) { - if (!ie || (ie && ie_version < 9)) prepareSelectAllHack() - let i = 0, poll = () => { - if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && - te.selectionEnd > 0 && input.prevInput == "\u200b") { - operation(cm, selectAll)(cm) - } else if (i++ < 10) { - display.detectingSelectAll = setTimeout(poll, 500) - } else { - display.selForContextMenu = null - display.input.reset() - } - } - display.detectingSelectAll = setTimeout(poll, 200) - } - } - - if (ie && ie_version >= 9) prepareSelectAllHack() - if (captureRightClick) { - e_stop(e) - let mouseup = () => { - off(window, "mouseup", mouseup) - setTimeout(rehide, 20) - } - on(window, "mouseup", mouseup) - } else { - setTimeout(rehide, 50) - } - } - - readOnlyChanged(val) { - if (!val) this.reset() - this.textarea.disabled = val == "nocursor" - this.textarea.readOnly = !!val - } - - setUneditable() {} -} - -TextareaInput.prototype.needsContentAttribute = false diff --git a/CODE/js/codemirror/src/input/indent.js b/CODE/js/codemirror/src/input/indent.js deleted file mode 100644 index c88772cb..00000000 --- a/CODE/js/codemirror/src/input/indent.js +++ /dev/null @@ -1,71 +0,0 @@ -import { getContextBefore } from "../line/highlight.js" -import { Pos } from "../line/pos.js" -import { getLine } from "../line/utils_line.js" -import { replaceRange } from "../model/changes.js" -import { Range } from "../model/selection.js" -import { replaceOneSelection } from "../model/selection_updates.js" -import { countColumn, Pass, spaceStr } from "../util/misc.js" - -// Indent the given line. The how parameter can be "smart", -// "add"/null, "subtract", or "prev". When aggressive is false -// (typically set to true for forced single-line indents), empty -// lines are not indented, and places where the mode returns Pass -// are left alone. -export function indentLine(cm, n, how, aggressive) { - let doc = cm.doc, state - if (how == null) how = "add" - if (how == "smart") { - // Fall back to "prev" when the mode doesn't have an indentation - // method. - if (!doc.mode.indent) how = "prev" - else state = getContextBefore(cm, n).state - } - - let tabSize = cm.options.tabSize - let line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) - if (line.stateAfter) line.stateAfter = null - let curSpaceString = line.text.match(/^\s*/)[0], indentation - if (!aggressive && !/\S/.test(line.text)) { - indentation = 0 - how = "not" - } else if (how == "smart") { - indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text) - if (indentation == Pass || indentation > 150) { - if (!aggressive) return - how = "prev" - } - } - if (how == "prev") { - if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize) - else indentation = 0 - } else if (how == "add") { - indentation = curSpace + cm.options.indentUnit - } else if (how == "subtract") { - indentation = curSpace - cm.options.indentUnit - } else if (typeof how == "number") { - indentation = curSpace + how - } - indentation = Math.max(0, indentation) - - let indentString = "", pos = 0 - if (cm.options.indentWithTabs) - for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} - if (pos < indentation) indentString += spaceStr(indentation - pos) - - if (indentString != curSpaceString) { - replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input") - line.stateAfter = null - return true - } else { - // Ensure that, if the cursor was in the whitespace at the start - // of the line, it is moved to the end of that space. - for (let i = 0; i < doc.sel.ranges.length; i++) { - let range = doc.sel.ranges[i] - if (range.head.line == n && range.head.ch < curSpaceString.length) { - let pos = Pos(n, curSpaceString.length) - replaceOneSelection(doc, i, new Range(pos, pos)) - break - } - } - } -} diff --git a/CODE/js/codemirror/src/input/input.js b/CODE/js/codemirror/src/input/input.js deleted file mode 100644 index 32adbf9b..00000000 --- a/CODE/js/codemirror/src/input/input.js +++ /dev/null @@ -1,135 +0,0 @@ -import { runInOp } from "../display/operations.js" -import { ensureCursorVisible } from "../display/scrolling.js" -import { Pos } from "../line/pos.js" -import { getLine } from "../line/utils_line.js" -import { makeChange } from "../model/changes.js" -import { ios, webkit } from "../util/browser.js" -import { elt } from "../util/dom.js" -import { lst, map } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" -import { splitLinesAuto } from "../util/feature_detection.js" - -import { indentLine } from "./indent.js" - -// This will be set to a {lineWise: bool, text: [string]} object, so -// that, when pasting, we know what kind of selections the copied -// text was made out of. -export let lastCopied = null - -export function setLastCopied(newLastCopied) { - lastCopied = newLastCopied -} - -export function applyTextInput(cm, inserted, deleted, sel, origin) { - let doc = cm.doc - cm.display.shift = false - if (!sel) sel = doc.sel - - let recent = +new Date - 200 - let paste = origin == "paste" || cm.state.pasteIncoming > recent - let textLines = splitLinesAuto(inserted), multiPaste = null - // When pasting N lines into N selections, insert one line per selection - if (paste && sel.ranges.length > 1) { - if (lastCopied && lastCopied.text.join("\n") == inserted) { - if (sel.ranges.length % lastCopied.text.length == 0) { - multiPaste = [] - for (let i = 0; i < lastCopied.text.length; i++) - multiPaste.push(doc.splitLines(lastCopied.text[i])) - } - } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { - multiPaste = map(textLines, l => [l]) - } - } - - let updateInput = cm.curOp.updateInput - // Normal behavior is to insert the new text into every selection - for (let i = sel.ranges.length - 1; i >= 0; i--) { - let range = sel.ranges[i] - let from = range.from(), to = range.to() - if (range.empty()) { - if (deleted && deleted > 0) // Handle deletion - from = Pos(from.line, from.ch - deleted) - else if (cm.state.overwrite && !paste) // Handle overwrite - to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) - else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) - from = to = Pos(from.line, 0) - } - let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, - origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")} - makeChange(cm.doc, changeEvent) - signalLater(cm, "inputRead", cm, changeEvent) - } - if (inserted && !paste) - triggerElectric(cm, inserted) - - ensureCursorVisible(cm) - if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput - cm.curOp.typing = true - cm.state.pasteIncoming = cm.state.cutIncoming = -1 -} - -export function handlePaste(e, cm) { - let pasted = e.clipboardData && e.clipboardData.getData("Text") - if (pasted) { - e.preventDefault() - if (!cm.isReadOnly() && !cm.options.disableInput) - runInOp(cm, () => applyTextInput(cm, pasted, 0, null, "paste")) - return true - } -} - -export function triggerElectric(cm, inserted) { - // When an 'electric' character is inserted, immediately trigger a reindent - if (!cm.options.electricChars || !cm.options.smartIndent) return - let sel = cm.doc.sel - - for (let i = sel.ranges.length - 1; i >= 0; i--) { - let range = sel.ranges[i] - if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue - let mode = cm.getModeAt(range.head) - let indented = false - if (mode.electricChars) { - for (let j = 0; j < mode.electricChars.length; j++) - if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { - indented = indentLine(cm, range.head.line, "smart") - break - } - } else if (mode.electricInput) { - if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) - indented = indentLine(cm, range.head.line, "smart") - } - if (indented) signalLater(cm, "electricInput", cm, range.head.line) - } -} - -export function copyableRanges(cm) { - let text = [], ranges = [] - for (let i = 0; i < cm.doc.sel.ranges.length; i++) { - let line = cm.doc.sel.ranges[i].head.line - let lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)} - ranges.push(lineRange) - text.push(cm.getRange(lineRange.anchor, lineRange.head)) - } - return {text: text, ranges: ranges} -} - -export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { - field.setAttribute("autocorrect", autocorrect ? "" : "off") - field.setAttribute("autocapitalize", autocapitalize ? "" : "off") - field.setAttribute("spellcheck", !!spellcheck) -} - -export function hiddenTextarea() { - let te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") - let div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") - // The textarea is kept positioned near the cursor to prevent the - // fact that it'll be scrolled into view on input from scrolling - // our fake cursor out of view. On webkit, when wrap=off, paste is - // very slow. So make the area wide instead. - if (webkit) te.style.width = "1000px" - else te.setAttribute("wrap", "off") - // If border: 0; -- iOS fails to open keyboard (issue #1287) - if (ios) te.style.border = "1px solid black" - disableBrowserMagic(te) - return div -} diff --git a/CODE/js/codemirror/src/input/keymap.js b/CODE/js/codemirror/src/input/keymap.js deleted file mode 100644 index f29f2bb1..00000000 --- a/CODE/js/codemirror/src/input/keymap.js +++ /dev/null @@ -1,148 +0,0 @@ -import { flipCtrlCmd, mac, presto } from "../util/browser.js" -import { map } from "../util/misc.js" - -import { keyNames } from "./keynames.js" - -export let keyMap = {} - -keyMap.basic = { - "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", - "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", - "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", - "Tab": "defaultTab", "Shift-Tab": "indentAuto", - "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", - "Esc": "singleSelection" -} -// Note that the save and find-related commands aren't defined by -// default. User code or addons can define them. Unknown commands -// are simply ignored. -keyMap.pcDefault = { - "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", - "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", - "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", - "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", - "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", - "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", - "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", - "fallthrough": "basic" -} -// Very basic readline/emacs-style bindings, which are standard on Mac. -keyMap.emacsy = { - "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", - "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", - "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", - "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", - "Ctrl-O": "openLine" -} -keyMap.macDefault = { - "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", - "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", - "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", - "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", - "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", - "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", - "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", - "fallthrough": ["basic", "emacsy"] -} -keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault - -// KEYMAP DISPATCH - -function normalizeKeyName(name) { - let parts = name.split(/-(?!$)/) - name = parts[parts.length - 1] - let alt, ctrl, shift, cmd - for (let i = 0; i < parts.length - 1; i++) { - let mod = parts[i] - if (/^(cmd|meta|m)$/i.test(mod)) cmd = true - else if (/^a(lt)?$/i.test(mod)) alt = true - else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true - else if (/^s(hift)?$/i.test(mod)) shift = true - else throw new Error("Unrecognized modifier name: " + mod) - } - if (alt) name = "Alt-" + name - if (ctrl) name = "Ctrl-" + name - if (cmd) name = "Cmd-" + name - if (shift) name = "Shift-" + name - return name -} - -// This is a kludge to keep keymaps mostly working as raw objects -// (backwards compatibility) while at the same time support features -// like normalization and multi-stroke key bindings. It compiles a -// new normalized keymap, and then updates the old object to reflect -// this. -export function normalizeKeyMap(keymap) { - let copy = {} - for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) { - let value = keymap[keyname] - if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue - if (value == "...") { delete keymap[keyname]; continue } - - let keys = map(keyname.split(" "), normalizeKeyName) - for (let i = 0; i < keys.length; i++) { - let val, name - if (i == keys.length - 1) { - name = keys.join(" ") - val = value - } else { - name = keys.slice(0, i + 1).join(" ") - val = "..." - } - let prev = copy[name] - if (!prev) copy[name] = val - else if (prev != val) throw new Error("Inconsistent bindings for " + name) - } - delete keymap[keyname] - } - for (let prop in copy) keymap[prop] = copy[prop] - return keymap -} - -export function lookupKey(key, map, handle, context) { - map = getKeyMap(map) - let found = map.call ? map.call(key, context) : map[key] - if (found === false) return "nothing" - if (found === "...") return "multi" - if (found != null && handle(found)) return "handled" - - if (map.fallthrough) { - if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") - return lookupKey(key, map.fallthrough, handle, context) - for (let i = 0; i < map.fallthrough.length; i++) { - let result = lookupKey(key, map.fallthrough[i], handle, context) - if (result) return result - } - } -} - -// Modifier key presses don't count as 'real' key presses for the -// purpose of keymap fallthrough. -export function isModifierKey(value) { - let name = typeof value == "string" ? value : keyNames[value.keyCode] - return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" -} - -export function addModifierNames(name, event, noShift) { - let base = name - if (event.altKey && base != "Alt") name = "Alt-" + name - if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name - if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") name = "Cmd-" + name - if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name - return name -} - -// Look up the name of a key as indicated by an event object. -export function keyName(event, noShift) { - if (presto && event.keyCode == 34 && event["char"]) return false - let name = keyNames[event.keyCode] - if (name == null || event.altGraphKey) return false - // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, - // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) - if (event.keyCode == 3 && event.code) name = event.code - return addModifierNames(name, event, noShift) -} - -export function getKeyMap(val) { - return typeof val == "string" ? keyMap[val] : val -} diff --git a/CODE/js/codemirror/src/input/keynames.js b/CODE/js/codemirror/src/input/keynames.js deleted file mode 100644 index eb09ca17..00000000 --- a/CODE/js/codemirror/src/input/keynames.js +++ /dev/null @@ -1,17 +0,0 @@ -export let keyNames = { - 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", - 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", - 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", - 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", - 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", - 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", - 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", - 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" -} - -// Number keys -for (let i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) -// Alphabetic keys -for (let i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) -// Function keys -for (let i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i diff --git a/CODE/js/codemirror/src/input/movement.js b/CODE/js/codemirror/src/input/movement.js deleted file mode 100644 index 479f221f..00000000 --- a/CODE/js/codemirror/src/input/movement.js +++ /dev/null @@ -1,111 +0,0 @@ -import { Pos } from "../line/pos.js" -import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement.js" -import { getBidiPartAt, getOrder } from "../util/bidi.js" -import { findFirst, lst, skipExtendingChars } from "../util/misc.js" - -function moveCharLogically(line, ch, dir) { - let target = skipExtendingChars(line.text, ch + dir, dir) - return target < 0 || target > line.text.length ? null : target -} - -export function moveLogically(line, start, dir) { - let ch = moveCharLogically(line, start.ch, dir) - return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") -} - -export function endOfLine(visually, cm, lineObj, lineNo, dir) { - if (visually) { - if (cm.doc.direction == "rtl") dir = -dir - let order = getOrder(lineObj, cm.doc.direction) - if (order) { - let part = dir < 0 ? lst(order) : order[0] - let moveInStorageOrder = (dir < 0) == (part.level == 1) - let sticky = moveInStorageOrder ? "after" : "before" - let ch - // With a wrapped rtl chunk (possibly spanning multiple bidi parts), - // it could be that the last bidi part is not on the last visual line, - // since visual lines contain content order-consecutive chunks. - // Thus, in rtl, we are looking for the first (content-order) character - // in the rtl chunk that is on the last line (that is, the same line - // as the last (content-order) character). - if (part.level > 0 || cm.doc.direction == "rtl") { - let prep = prepareMeasureForLine(cm, lineObj) - ch = dir < 0 ? lineObj.text.length - 1 : 0 - let targetTop = measureCharPrepared(cm, prep, ch).top - ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) - if (sticky == "before") ch = moveCharLogically(lineObj, ch, 1) - } else ch = dir < 0 ? part.to : part.from - return new Pos(lineNo, ch, sticky) - } - } - return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") -} - -export function moveVisually(cm, line, start, dir) { - let bidi = getOrder(line, cm.doc.direction) - if (!bidi) return moveLogically(line, start, dir) - if (start.ch >= line.text.length) { - start.ch = line.text.length - start.sticky = "before" - } else if (start.ch <= 0) { - start.ch = 0 - start.sticky = "after" - } - let partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos] - if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { - // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, - // nothing interesting happens. - return moveLogically(line, start, dir) - } - - let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir) - let prep - let getWrappedLineExtent = ch => { - if (!cm.options.lineWrapping) return {begin: 0, end: line.text.length} - prep = prep || prepareMeasureForLine(cm, line) - return wrappedLineExtentChar(cm, line, prep, ch) - } - let wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch) - - if (cm.doc.direction == "rtl" || part.level == 1) { - let moveInStorageOrder = (part.level == 1) == (dir < 0) - let ch = mv(start, moveInStorageOrder ? 1 : -1) - if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { - // Case 2: We move within an rtl part or in an rtl editor on the same visual line - let sticky = moveInStorageOrder ? "before" : "after" - return new Pos(start.line, ch, sticky) - } - } - - // Case 3: Could not move within this bidi part in this visual line, so leave - // the current bidi part - - let searchInVisualLine = (partPos, dir, wrappedLineExtent) => { - let getRes = (ch, moveInStorageOrder) => moveInStorageOrder - ? new Pos(start.line, mv(ch, 1), "before") - : new Pos(start.line, ch, "after") - - for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { - let part = bidi[partPos] - let moveInStorageOrder = (dir > 0) == (part.level != 1) - let ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1) - if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder) - ch = moveInStorageOrder ? part.from : mv(part.to, -1) - if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) return getRes(ch, moveInStorageOrder) - } - } - - // Case 3a: Look for other bidi parts on the same visual line - let res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent) - if (res) return res - - // Case 3b: Look for other bidi parts on the next visual line - let nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1) - if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { - res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)) - if (res) return res - } - - // Case 4: Nowhere to move - return null -} diff --git a/CODE/js/codemirror/src/line/highlight.js b/CODE/js/codemirror/src/line/highlight.js deleted file mode 100644 index 7b4ca0b3..00000000 --- a/CODE/js/codemirror/src/line/highlight.js +++ /dev/null @@ -1,284 +0,0 @@ -import { countColumn } from "../util/misc.js" -import { copyState, innerMode, startState } from "../modes.js" -import StringStream from "../util/StringStream.js" - -import { getLine, lineNo } from "./utils_line.js" -import { clipPos } from "./pos.js" - -class SavedContext { - constructor(state, lookAhead) { - this.state = state - this.lookAhead = lookAhead - } -} - -class Context { - constructor(doc, state, line, lookAhead) { - this.state = state - this.doc = doc - this.line = line - this.maxLookAhead = lookAhead || 0 - this.baseTokens = null - this.baseTokenPos = 1 - } - - lookAhead(n) { - let line = this.doc.getLine(this.line + n) - if (line != null && n > this.maxLookAhead) this.maxLookAhead = n - return line - } - - baseToken(n) { - if (!this.baseTokens) return null - while (this.baseTokens[this.baseTokenPos] <= n) - this.baseTokenPos += 2 - let type = this.baseTokens[this.baseTokenPos + 1] - return {type: type && type.replace(/( |^)overlay .*/, ""), - size: this.baseTokens[this.baseTokenPos] - n} - } - - nextLine() { - this.line++ - if (this.maxLookAhead > 0) this.maxLookAhead-- - } - - static fromSaved(doc, saved, line) { - if (saved instanceof SavedContext) - return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) - else - return new Context(doc, copyState(doc.mode, saved), line) - } - - save(copy) { - let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state - return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state - } -} - - -// Compute a style array (an array starting with a mode generation -// -- for invalidation -- followed by pairs of end positions and -// style strings), which is used to highlight the tokens on the -// line. -export function highlightLine(cm, line, context, forceToEnd) { - // A styles array always starts with a number identifying the - // mode/overlays that it is based on (for easy invalidation). - let st = [cm.state.modeGen], lineClasses = {} - // Compute the base array of styles - runMode(cm, line.text, cm.doc.mode, context, (end, style) => st.push(end, style), - lineClasses, forceToEnd) - let state = context.state - - // Run overlays, adjust style array. - for (let o = 0; o < cm.state.overlays.length; ++o) { - context.baseTokens = st - let overlay = cm.state.overlays[o], i = 1, at = 0 - context.state = true - runMode(cm, line.text, overlay.mode, context, (end, style) => { - let start = i - // Ensure there's a token end at the current position, and that i points at it - while (at < end) { - let i_end = st[i] - if (i_end > end) - st.splice(i, 1, end, st[i+1], i_end) - i += 2 - at = Math.min(end, i_end) - } - if (!style) return - if (overlay.opaque) { - st.splice(start, i - start, end, "overlay " + style) - i = start + 2 - } else { - for (; start < i; start += 2) { - let cur = st[start+1] - st[start+1] = (cur ? cur + " " : "") + "overlay " + style - } - } - }, lineClasses) - context.state = state - context.baseTokens = null - context.baseTokenPos = 1 - } - - return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} -} - -export function getLineStyles(cm, line, updateFrontier) { - if (!line.styles || line.styles[0] != cm.state.modeGen) { - let context = getContextBefore(cm, lineNo(line)) - let resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state) - let result = highlightLine(cm, line, context) - if (resetState) context.state = resetState - line.stateAfter = context.save(!resetState) - line.styles = result.styles - if (result.classes) line.styleClasses = result.classes - else if (line.styleClasses) line.styleClasses = null - if (updateFrontier === cm.doc.highlightFrontier) - cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) - } - return line.styles -} - -export function getContextBefore(cm, n, precise) { - let doc = cm.doc, display = cm.display - if (!doc.mode.startState) return new Context(doc, true, n) - let start = findStartLine(cm, n, precise) - let saved = start > doc.first && getLine(doc, start - 1).stateAfter - let context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start) - - doc.iter(start, n, line => { - processLine(cm, line.text, context) - let pos = context.line - line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null - context.nextLine() - }) - if (precise) doc.modeFrontier = context.line - return context -} - -// Lightweight form of highlight -- proceed over this line and -// update state, but don't save a style array. Used for lines that -// aren't currently visible. -export function processLine(cm, text, context, startAt) { - let mode = cm.doc.mode - let stream = new StringStream(text, cm.options.tabSize, context) - stream.start = stream.pos = startAt || 0 - if (text == "") callBlankLine(mode, context.state) - while (!stream.eol()) { - readToken(mode, stream, context.state) - stream.start = stream.pos - } -} - -function callBlankLine(mode, state) { - if (mode.blankLine) return mode.blankLine(state) - if (!mode.innerMode) return - let inner = innerMode(mode, state) - if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) -} - -function readToken(mode, stream, state, inner) { - for (let i = 0; i < 10; i++) { - if (inner) inner[0] = innerMode(mode, state).mode - let style = mode.token(stream, state) - if (stream.pos > stream.start) return style - } - throw new Error("Mode " + mode.name + " failed to advance stream.") -} - -class Token { - constructor(stream, type, state) { - this.start = stream.start; this.end = stream.pos - this.string = stream.current() - this.type = type || null - this.state = state - } -} - -// Utility for getTokenAt and getLineTokens -export function takeToken(cm, pos, precise, asArray) { - let doc = cm.doc, mode = doc.mode, style - pos = clipPos(doc, pos) - let line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise) - let stream = new StringStream(line.text, cm.options.tabSize, context), tokens - if (asArray) tokens = [] - while ((asArray || stream.pos < pos.ch) && !stream.eol()) { - stream.start = stream.pos - style = readToken(mode, stream, context.state) - if (asArray) tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) - } - return asArray ? tokens : new Token(stream, style, context.state) -} - -function extractLineClasses(type, output) { - if (type) for (;;) { - let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) - if (!lineClass) break - type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) - let prop = lineClass[1] ? "bgClass" : "textClass" - if (output[prop] == null) - output[prop] = lineClass[2] - else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) - output[prop] += " " + lineClass[2] - } - return type -} - -// Run the given mode's parser over a line, calling f for each token. -function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { - let flattenSpans = mode.flattenSpans - if (flattenSpans == null) flattenSpans = cm.options.flattenSpans - let curStart = 0, curStyle = null - let stream = new StringStream(text, cm.options.tabSize, context), style - let inner = cm.options.addModeClass && [null] - if (text == "") extractLineClasses(callBlankLine(mode, context.state), lineClasses) - while (!stream.eol()) { - if (stream.pos > cm.options.maxHighlightLength) { - flattenSpans = false - if (forceToEnd) processLine(cm, text, context, stream.pos) - stream.pos = text.length - style = null - } else { - style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses) - } - if (inner) { - let mName = inner[0].name - if (mName) style = "m-" + (style ? mName + " " + style : mName) - } - if (!flattenSpans || curStyle != style) { - while (curStart < stream.start) { - curStart = Math.min(stream.start, curStart + 5000) - f(curStart, curStyle) - } - curStyle = style - } - stream.start = stream.pos - } - while (curStart < stream.pos) { - // Webkit seems to refuse to render text nodes longer than 57444 - // characters, and returns inaccurate measurements in nodes - // starting around 5000 chars. - let pos = Math.min(stream.pos, curStart + 5000) - f(pos, curStyle) - curStart = pos - } -} - -// Finds the line to start with when starting a parse. Tries to -// find a line with a stateAfter, so that it can start with a -// valid state. If that fails, it returns the line with the -// smallest indentation, which tends to need the least context to -// parse correctly. -function findStartLine(cm, n, precise) { - let minindent, minline, doc = cm.doc - let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) - for (let search = n; search > lim; --search) { - if (search <= doc.first) return doc.first - let line = getLine(doc, search - 1), after = line.stateAfter - if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) - return search - let indented = countColumn(line.text, null, cm.options.tabSize) - if (minline == null || minindent > indented) { - minline = search - 1 - minindent = indented - } - } - return minline -} - -export function retreatFrontier(doc, n) { - doc.modeFrontier = Math.min(doc.modeFrontier, n) - if (doc.highlightFrontier < n - 10) return - let start = doc.first - for (let line = n - 1; line > start; line--) { - let saved = getLine(doc, line).stateAfter - // change is on 3 - // state on line 1 looked ahead 2 -- so saw 3 - // test 1 + 2 < 3 should cover this - if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { - start = line + 1 - break - } - } - doc.highlightFrontier = Math.min(doc.highlightFrontier, start) -} diff --git a/CODE/js/codemirror/src/line/line_data.js b/CODE/js/codemirror/src/line/line_data.js deleted file mode 100644 index e650b3e3..00000000 --- a/CODE/js/codemirror/src/line/line_data.js +++ /dev/null @@ -1,349 +0,0 @@ -import { getOrder } from "../util/bidi.js" -import { ie, ie_version, webkit } from "../util/browser.js" -import { elt, eltP, joinClasses } from "../util/dom.js" -import { eventMixin, signal } from "../util/event.js" -import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js" -import { lst, spaceStr } from "../util/misc.js" - -import { getLineStyles } from "./highlight.js" -import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js" -import { getLine, lineNo, updateLineHeight } from "./utils_line.js" - -// LINE DATA STRUCTURE - -// Line objects. These hold state related to a line, including -// highlighting info (the styles array). -export class Line { - constructor(text, markedSpans, estimateHeight) { - this.text = text - attachMarkedSpans(this, markedSpans) - this.height = estimateHeight ? estimateHeight(this) : 1 - } - - lineNo() { return lineNo(this) } -} -eventMixin(Line) - -// Change the content (text, markers) of a line. Automatically -// invalidates cached information and tries to re-estimate the -// line's height. -export function updateLine(line, text, markedSpans, estimateHeight) { - line.text = text - if (line.stateAfter) line.stateAfter = null - if (line.styles) line.styles = null - if (line.order != null) line.order = null - detachMarkedSpans(line) - attachMarkedSpans(line, markedSpans) - let estHeight = estimateHeight ? estimateHeight(line) : 1 - if (estHeight != line.height) updateLineHeight(line, estHeight) -} - -// Detach a line from the document tree and its markers. -export function cleanUpLine(line) { - line.parent = null - detachMarkedSpans(line) -} - -// Convert a style as returned by a mode (either null, or a string -// containing one or more styles) to a CSS style. This is cached, -// and also looks for line-wide styles. -let styleToClassCache = {}, styleToClassCacheWithMode = {} -function interpretTokenStyle(style, options) { - if (!style || /^\s*$/.test(style)) return null - let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache - return cache[style] || - (cache[style] = style.replace(/\S+/g, "cm-$&")) -} - -// Render the DOM representation of the text of a line. Also builds -// up a 'line map', which points at the DOM nodes that represent -// specific stretches of text, and is used by the measuring code. -// The returned object contains the DOM node, this map, and -// information about line-wide styles that were set by the mode. -export function buildLineContent(cm, lineView) { - // The padding-right forces the element to have a 'border', which - // is needed on Webkit to be able to get line-level bounding - // rectangles for it (in measureChar). - let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null) - let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, - col: 0, pos: 0, cm: cm, - trailingSpace: false, - splitSpaces: cm.getOption("lineWrapping")} - lineView.measure = {} - - // Iterate over the logical lines that make up this visual line. - for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { - let line = i ? lineView.rest[i - 1] : lineView.line, order - builder.pos = 0 - builder.addToken = buildToken - // Optionally wire in some hacks into the token-rendering - // algorithm, to deal with browser quirks. - if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) - builder.addToken = buildTokenBadBidi(builder.addToken, order) - builder.map = [] - let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) - insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) - if (line.styleClasses) { - if (line.styleClasses.bgClass) - builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") - if (line.styleClasses.textClass) - builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") - } - - // Ensure at least a single node is present, for measuring. - if (builder.map.length == 0) - builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) - - // Store the map and a cache object for the current logical line - if (i == 0) { - lineView.measure.map = builder.map - lineView.measure.cache = {} - } else { - ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) - ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) - } - } - - // See issue #2901 - if (webkit) { - let last = builder.content.lastChild - if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) - builder.content.className = "cm-tab-wrap-hack" - } - - signal(cm, "renderLine", cm, lineView.line, builder.pre) - if (builder.pre.className) - builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") - - return builder -} - -export function defaultSpecialCharPlaceholder(ch) { - let token = elt("span", "\u2022", "cm-invalidchar") - token.title = "\\u" + ch.charCodeAt(0).toString(16) - token.setAttribute("aria-label", token.title) - return token -} - -// Build up the DOM representation for a single token, and add it to -// the line map. Takes care to render special characters separately. -function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { - if (!text) return - let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text - let special = builder.cm.state.specialChars, mustWrap = false - let content - if (!special.test(text)) { - builder.col += text.length - content = document.createTextNode(displayText) - builder.map.push(builder.pos, builder.pos + text.length, content) - if (ie && ie_version < 9) mustWrap = true - builder.pos += text.length - } else { - content = document.createDocumentFragment() - let pos = 0 - while (true) { - special.lastIndex = pos - let m = special.exec(text) - let skipped = m ? m.index - pos : text.length - pos - if (skipped) { - let txt = document.createTextNode(displayText.slice(pos, pos + skipped)) - if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) - else content.appendChild(txt) - builder.map.push(builder.pos, builder.pos + skipped, txt) - builder.col += skipped - builder.pos += skipped - } - if (!m) break - pos += skipped + 1 - let txt - if (m[0] == "\t") { - let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize - txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) - txt.setAttribute("role", "presentation") - txt.setAttribute("cm-text", "\t") - builder.col += tabWidth - } else if (m[0] == "\r" || m[0] == "\n") { - txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) - txt.setAttribute("cm-text", m[0]) - builder.col += 1 - } else { - txt = builder.cm.options.specialCharPlaceholder(m[0]) - txt.setAttribute("cm-text", m[0]) - if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) - else content.appendChild(txt) - builder.col += 1 - } - builder.map.push(builder.pos, builder.pos + 1, txt) - builder.pos++ - } - } - builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 - if (style || startStyle || endStyle || mustWrap || css || attributes) { - let fullStyle = style || "" - if (startStyle) fullStyle += startStyle - if (endStyle) fullStyle += endStyle - let token = elt("span", [content], fullStyle, css) - if (attributes) { - for (let attr in attributes) if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") - token.setAttribute(attr, attributes[attr]) - } - return builder.content.appendChild(token) - } - builder.content.appendChild(content) -} - -// Change some spaces to NBSP to prevent the browser from collapsing -// trailing spaces at the end of a line when rendering text (issue #1362). -function splitSpaces(text, trailingBefore) { - if (text.length > 1 && !/ /.test(text)) return text - let spaceBefore = trailingBefore, result = "" - for (let i = 0; i < text.length; i++) { - let ch = text.charAt(i) - if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) - ch = "\u00a0" - result += ch - spaceBefore = ch == " " - } - return result -} - -// Work around nonsense dimensions being reported for stretches of -// right-to-left text. -function buildTokenBadBidi(inner, order) { - return (builder, text, style, startStyle, endStyle, css, attributes) => { - style = style ? style + " cm-force-border" : "cm-force-border" - let start = builder.pos, end = start + text.length - for (;;) { - // Find the part that overlaps with the start of this text - let part - for (let i = 0; i < order.length; i++) { - part = order[i] - if (part.to > start && part.from <= start) break - } - if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, css, attributes) - inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes) - startStyle = null - text = text.slice(part.to - start) - start = part.to - } - } -} - -function buildCollapsedSpan(builder, size, marker, ignoreWidget) { - let widget = !ignoreWidget && marker.widgetNode - if (widget) builder.map.push(builder.pos, builder.pos + size, widget) - if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { - if (!widget) - widget = builder.content.appendChild(document.createElement("span")) - widget.setAttribute("cm-marker", marker.id) - } - if (widget) { - builder.cm.display.input.setUneditable(widget) - builder.content.appendChild(widget) - } - builder.pos += size - builder.trailingSpace = false -} - -// Outputs a number of spans to make up a line, taking highlighting -// and marked text into account. -function insertLineContent(line, builder, styles) { - let spans = line.markedSpans, allText = line.text, at = 0 - if (!spans) { - for (let i = 1; i < styles.length; i+=2) - builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) - return - } - - let len = allText.length, pos = 0, i = 1, text = "", style, css - let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes - for (;;) { - if (nextChange == pos) { // Update current marker set - spanStyle = spanEndStyle = spanStartStyle = css = "" - attributes = null - collapsed = null; nextChange = Infinity - let foundBookmarks = [], endStyles - for (let j = 0; j < spans.length; ++j) { - let sp = spans[j], m = sp.marker - if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { - foundBookmarks.push(m) - } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { - if (sp.to != null && sp.to != pos && nextChange > sp.to) { - nextChange = sp.to - spanEndStyle = "" - } - if (m.className) spanStyle += " " + m.className - if (m.css) css = (css ? css + ";" : "") + m.css - if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle - if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) - // support for the old title property - // https://github.com/codemirror/CodeMirror/pull/5673 - if (m.title) (attributes || (attributes = {})).title = m.title - if (m.attributes) { - for (let attr in m.attributes) - (attributes || (attributes = {}))[attr] = m.attributes[attr] - } - if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) - collapsed = sp - } else if (sp.from > pos && nextChange > sp.from) { - nextChange = sp.from - } - } - if (endStyles) for (let j = 0; j < endStyles.length; j += 2) - if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] - - if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j) - buildCollapsedSpan(builder, 0, foundBookmarks[j]) - if (collapsed && (collapsed.from || 0) == pos) { - buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, - collapsed.marker, collapsed.from == null) - if (collapsed.to == null) return - if (collapsed.to == pos) collapsed = false - } - } - if (pos >= len) break - - let upto = Math.min(len, nextChange) - while (true) { - if (text) { - let end = pos + text.length - if (!collapsed) { - let tokenText = end > upto ? text.slice(0, upto - pos) : text - builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes) - } - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} - pos = end - spanStartStyle = "" - } - text = allText.slice(at, at = styles[i++]) - style = interpretTokenStyle(styles[i++], builder.cm.options) - } - } -} - - -// These objects are used to represent the visible (currently drawn) -// part of the document. A LineView may correspond to multiple -// logical lines, if those are connected by collapsed ranges. -export function LineView(doc, line, lineN) { - // The starting line - this.line = line - // Continuing lines, if any - this.rest = visualLineContinued(line) - // Number of logical lines in this visual line - this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1 - this.node = this.text = null - this.hidden = lineIsHidden(doc, line) -} - -// Create a range of LineView objects for the given lines. -export function buildViewArray(cm, from, to) { - let array = [], nextPos - for (let pos = from; pos < to; pos = nextPos) { - let view = new LineView(cm.doc, getLine(cm.doc, pos), pos) - nextPos = pos + view.size - array.push(view) - } - return array -} diff --git a/CODE/js/codemirror/src/line/pos.js b/CODE/js/codemirror/src/line/pos.js deleted file mode 100644 index 2a498f8f..00000000 --- a/CODE/js/codemirror/src/line/pos.js +++ /dev/null @@ -1,40 +0,0 @@ -import { getLine } from "./utils_line.js" - -// A Pos instance represents a position within the text. -export function Pos(line, ch, sticky = null) { - if (!(this instanceof Pos)) return new Pos(line, ch, sticky) - this.line = line - this.ch = ch - this.sticky = sticky -} - -// Compare two positions, return 0 if they are the same, a negative -// number when a is less, and a positive number otherwise. -export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } - -export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } - -export function copyPos(x) {return Pos(x.line, x.ch)} -export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } -export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } - -// Most of the external API clips given positions to make sure they -// actually exist within the document. -export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} -export function clipPos(doc, pos) { - if (pos.line < doc.first) return Pos(doc.first, 0) - let last = doc.first + doc.size - 1 - if (pos.line > last) return Pos(last, getLine(doc, last).text.length) - return clipToLen(pos, getLine(doc, pos.line).text.length) -} -function clipToLen(pos, linelen) { - let ch = pos.ch - if (ch == null || ch > linelen) return Pos(pos.line, linelen) - else if (ch < 0) return Pos(pos.line, 0) - else return pos -} -export function clipPosArray(doc, array) { - let out = [] - for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) - return out -} diff --git a/CODE/js/codemirror/src/line/saw_special_spans.js b/CODE/js/codemirror/src/line/saw_special_spans.js deleted file mode 100644 index d315e7ba..00000000 --- a/CODE/js/codemirror/src/line/saw_special_spans.js +++ /dev/null @@ -1,10 +0,0 @@ -// Optimize some code when these features are not used. -export let sawReadOnlySpans = false, sawCollapsedSpans = false - -export function seeReadOnlySpans() { - sawReadOnlySpans = true -} - -export function seeCollapsedSpans() { - sawCollapsedSpans = true -} diff --git a/CODE/js/codemirror/src/line/spans.js b/CODE/js/codemirror/src/line/spans.js deleted file mode 100644 index d81dec4b..00000000 --- a/CODE/js/codemirror/src/line/spans.js +++ /dev/null @@ -1,382 +0,0 @@ -import { indexOf, lst } from "../util/misc.js" - -import { cmp } from "./pos.js" -import { sawCollapsedSpans } from "./saw_special_spans.js" -import { getLine, isLine, lineNo } from "./utils_line.js" - -// TEXTMARKER SPANS - -export function MarkedSpan(marker, from, to) { - this.marker = marker - this.from = from; this.to = to -} - -// Search an array of spans for a span matching the given marker. -export function getMarkedSpanFor(spans, marker) { - if (spans) for (let i = 0; i < spans.length; ++i) { - let span = spans[i] - if (span.marker == marker) return span - } -} -// Remove a span from an array, returning undefined if no spans are -// left (we don't store arrays for lines without spans). -export function removeMarkedSpan(spans, span) { - let r - for (let i = 0; i < spans.length; ++i) - if (spans[i] != span) (r || (r = [])).push(spans[i]) - return r -} -// Add a span to a line. -export function addMarkedSpan(line, span) { - line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span] - span.marker.attachLine(line) -} - -// Used for the algorithm that adjusts markers for a change in the -// document. These functions cut an array of spans at a given -// character position, returning an array of remaining chunks (or -// undefined if nothing remains). -function markedSpansBefore(old, startCh, isInsert) { - let nw - if (old) for (let i = 0; i < old.length; ++i) { - let span = old[i], marker = span.marker - let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) - if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { - let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) - ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) - } - } - return nw -} -function markedSpansAfter(old, endCh, isInsert) { - let nw - if (old) for (let i = 0; i < old.length; ++i) { - let span = old[i], marker = span.marker - let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) - if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { - let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) - ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, - span.to == null ? null : span.to - endCh)) - } - } - return nw -} - -// Given a change object, compute the new set of marker spans that -// cover the line in which the change took place. Removes spans -// entirely within the change, reconnects spans belonging to the -// same marker that appear on both sides of the change, and cuts off -// spans partially within the change. Returns an array of span -// arrays with one element for each line in (after) the change. -export function stretchSpansOverChange(doc, change) { - if (change.full) return null - let oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans - let oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans - if (!oldFirst && !oldLast) return null - - let startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 - // Get the spans that 'stick out' on both sides - let first = markedSpansBefore(oldFirst, startCh, isInsert) - let last = markedSpansAfter(oldLast, endCh, isInsert) - - // Next, merge those two ends - let sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0) - if (first) { - // Fix up .to properties of first - for (let i = 0; i < first.length; ++i) { - let span = first[i] - if (span.to == null) { - let found = getMarkedSpanFor(last, span.marker) - if (!found) span.to = startCh - else if (sameLine) span.to = found.to == null ? null : found.to + offset - } - } - } - if (last) { - // Fix up .from in last (or move them into first in case of sameLine) - for (let i = 0; i < last.length; ++i) { - let span = last[i] - if (span.to != null) span.to += offset - if (span.from == null) { - let found = getMarkedSpanFor(first, span.marker) - if (!found) { - span.from = offset - if (sameLine) (first || (first = [])).push(span) - } - } else { - span.from += offset - if (sameLine) (first || (first = [])).push(span) - } - } - } - // Make sure we didn't create any zero-length spans - if (first) first = clearEmptySpans(first) - if (last && last != first) last = clearEmptySpans(last) - - let newMarkers = [first] - if (!sameLine) { - // Fill gap with whole-line-spans - let gap = change.text.length - 2, gapMarkers - if (gap > 0 && first) - for (let i = 0; i < first.length; ++i) - if (first[i].to == null) - (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)) - for (let i = 0; i < gap; ++i) - newMarkers.push(gapMarkers) - newMarkers.push(last) - } - return newMarkers -} - -// Remove spans that are empty and don't have a clearWhenEmpty -// option of false. -function clearEmptySpans(spans) { - for (let i = 0; i < spans.length; ++i) { - let span = spans[i] - if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) - spans.splice(i--, 1) - } - if (!spans.length) return null - return spans -} - -// Used to 'clip' out readOnly ranges when making a change. -export function removeReadOnlyRanges(doc, from, to) { - let markers = null - doc.iter(from.line, to.line + 1, line => { - if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { - let mark = line.markedSpans[i].marker - if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) - (markers || (markers = [])).push(mark) - } - }) - if (!markers) return null - let parts = [{from: from, to: to}] - for (let i = 0; i < markers.length; ++i) { - let mk = markers[i], m = mk.find(0) - for (let j = 0; j < parts.length; ++j) { - let p = parts[j] - if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue - let newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) - if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) - newParts.push({from: p.from, to: m.from}) - if (dto > 0 || !mk.inclusiveRight && !dto) - newParts.push({from: m.to, to: p.to}) - parts.splice.apply(parts, newParts) - j += newParts.length - 3 - } - } - return parts -} - -// Connect or disconnect spans from a line. -export function detachMarkedSpans(line) { - let spans = line.markedSpans - if (!spans) return - for (let i = 0; i < spans.length; ++i) - spans[i].marker.detachLine(line) - line.markedSpans = null -} -export function attachMarkedSpans(line, spans) { - if (!spans) return - for (let i = 0; i < spans.length; ++i) - spans[i].marker.attachLine(line) - line.markedSpans = spans -} - -// Helpers used when computing which overlapping collapsed span -// counts as the larger one. -function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } -function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } - -// Returns a number indicating which of two overlapping collapsed -// spans is larger (and thus includes the other). Falls back to -// comparing ids when the spans cover exactly the same range. -export function compareCollapsedMarkers(a, b) { - let lenDiff = a.lines.length - b.lines.length - if (lenDiff != 0) return lenDiff - let aPos = a.find(), bPos = b.find() - let fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) - if (fromCmp) return -fromCmp - let toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) - if (toCmp) return toCmp - return b.id - a.id -} - -// Find out whether a line ends or starts in a collapsed span. If -// so, return the marker for that span. -function collapsedSpanAtSide(line, start) { - let sps = sawCollapsedSpans && line.markedSpans, found - if (sps) for (let sp, i = 0; i < sps.length; ++i) { - sp = sps[i] - if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && - (!found || compareCollapsedMarkers(found, sp.marker) < 0)) - found = sp.marker - } - return found -} -export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } -export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } - -export function collapsedSpanAround(line, ch) { - let sps = sawCollapsedSpans && line.markedSpans, found - if (sps) for (let i = 0; i < sps.length; ++i) { - let sp = sps[i] - if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && - (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker - } - return found -} - -// Test whether there exists a collapsed span that partially -// overlaps (covers the start or end, but not both) of a new span. -// Such overlap is not allowed. -export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { - let line = getLine(doc, lineNo) - let sps = sawCollapsedSpans && line.markedSpans - if (sps) for (let i = 0; i < sps.length; ++i) { - let sp = sps[i] - if (!sp.marker.collapsed) continue - let found = sp.marker.find(0) - let fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) - let toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) - if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue - if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || - fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) - return true - } -} - -// A visual line is a line as drawn on the screen. Folding, for -// example, can cause multiple logical lines to appear on the same -// visual line. This finds the start of the visual line that the -// given line is part of (usually that is the line itself). -export function visualLine(line) { - let merged - while (merged = collapsedSpanAtStart(line)) - line = merged.find(-1, true).line - return line -} - -export function visualLineEnd(line) { - let merged - while (merged = collapsedSpanAtEnd(line)) - line = merged.find(1, true).line - return line -} - -// Returns an array of logical lines that continue the visual line -// started by the argument, or undefined if there are no such lines. -export function visualLineContinued(line) { - let merged, lines - while (merged = collapsedSpanAtEnd(line)) { - line = merged.find(1, true).line - ;(lines || (lines = [])).push(line) - } - return lines -} - -// Get the line number of the start of the visual line that the -// given line number is part of. -export function visualLineNo(doc, lineN) { - let line = getLine(doc, lineN), vis = visualLine(line) - if (line == vis) return lineN - return lineNo(vis) -} - -// Get the line number of the start of the next visual line after -// the given line. -export function visualLineEndNo(doc, lineN) { - if (lineN > doc.lastLine()) return lineN - let line = getLine(doc, lineN), merged - if (!lineIsHidden(doc, line)) return lineN - while (merged = collapsedSpanAtEnd(line)) - line = merged.find(1, true).line - return lineNo(line) + 1 -} - -// Compute whether a line is hidden. Lines count as hidden when they -// are part of a visual line that starts with another line, or when -// they are entirely covered by collapsed, non-widget span. -export function lineIsHidden(doc, line) { - let sps = sawCollapsedSpans && line.markedSpans - if (sps) for (let sp, i = 0; i < sps.length; ++i) { - sp = sps[i] - if (!sp.marker.collapsed) continue - if (sp.from == null) return true - if (sp.marker.widgetNode) continue - if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) - return true - } -} -function lineIsHiddenInner(doc, line, span) { - if (span.to == null) { - let end = span.marker.find(1, true) - return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) - } - if (span.marker.inclusiveRight && span.to == line.text.length) - return true - for (let sp, i = 0; i < line.markedSpans.length; ++i) { - sp = line.markedSpans[i] - if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && - (sp.to == null || sp.to != span.from) && - (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && - lineIsHiddenInner(doc, line, sp)) return true - } -} - -// Find the height above the given line. -export function heightAtLine(lineObj) { - lineObj = visualLine(lineObj) - - let h = 0, chunk = lineObj.parent - for (let i = 0; i < chunk.lines.length; ++i) { - let line = chunk.lines[i] - if (line == lineObj) break - else h += line.height - } - for (let p = chunk.parent; p; chunk = p, p = chunk.parent) { - for (let i = 0; i < p.children.length; ++i) { - let cur = p.children[i] - if (cur == chunk) break - else h += cur.height - } - } - return h -} - -// Compute the character length of a line, taking into account -// collapsed ranges (see markText) that might hide parts, and join -// other lines onto it. -export function lineLength(line) { - if (line.height == 0) return 0 - let len = line.text.length, merged, cur = line - while (merged = collapsedSpanAtStart(cur)) { - let found = merged.find(0, true) - cur = found.from.line - len += found.from.ch - found.to.ch - } - cur = line - while (merged = collapsedSpanAtEnd(cur)) { - let found = merged.find(0, true) - len -= cur.text.length - found.from.ch - cur = found.to.line - len += cur.text.length - found.to.ch - } - return len -} - -// Find the longest line in the document. -export function findMaxLine(cm) { - let d = cm.display, doc = cm.doc - d.maxLine = getLine(doc, doc.first) - d.maxLineLength = lineLength(d.maxLine) - d.maxLineChanged = true - doc.iter(line => { - let len = lineLength(line) - if (len > d.maxLineLength) { - d.maxLineLength = len - d.maxLine = line - } - }) -} diff --git a/CODE/js/codemirror/src/line/utils_line.js b/CODE/js/codemirror/src/line/utils_line.js deleted file mode 100644 index c8862943..00000000 --- a/CODE/js/codemirror/src/line/utils_line.js +++ /dev/null @@ -1,85 +0,0 @@ -import { indexOf } from "../util/misc.js" - -// Find the line object corresponding to the given line number. -export function getLine(doc, n) { - n -= doc.first - if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") - let chunk = doc - while (!chunk.lines) { - for (let i = 0;; ++i) { - let child = chunk.children[i], sz = child.chunkSize() - if (n < sz) { chunk = child; break } - n -= sz - } - } - return chunk.lines[n] -} - -// Get the part of a document between two positions, as an array of -// strings. -export function getBetween(doc, start, end) { - let out = [], n = start.line - doc.iter(start.line, end.line + 1, line => { - let text = line.text - if (n == end.line) text = text.slice(0, end.ch) - if (n == start.line) text = text.slice(start.ch) - out.push(text) - ++n - }) - return out -} -// Get the lines between from and to, as array of strings. -export function getLines(doc, from, to) { - let out = [] - doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value - return out -} - -// Update the height of a line, propagating the height change -// upwards to parent nodes. -export function updateLineHeight(line, height) { - let diff = height - line.height - if (diff) for (let n = line; n; n = n.parent) n.height += diff -} - -// Given a line object, find its line number by walking up through -// its parent links. -export function lineNo(line) { - if (line.parent == null) return null - let cur = line.parent, no = indexOf(cur.lines, line) - for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { - for (let i = 0;; ++i) { - if (chunk.children[i] == cur) break - no += chunk.children[i].chunkSize() - } - } - return no + cur.first -} - -// Find the line at the given vertical position, using the height -// information in the document tree. -export function lineAtHeight(chunk, h) { - let n = chunk.first - outer: do { - for (let i = 0; i < chunk.children.length; ++i) { - let child = chunk.children[i], ch = child.height - if (h < ch) { chunk = child; continue outer } - h -= ch - n += child.chunkSize() - } - return n - } while (!chunk.lines) - let i = 0 - for (; i < chunk.lines.length; ++i) { - let line = chunk.lines[i], lh = line.height - if (h < lh) break - h -= lh - } - return n + i -} - -export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} - -export function lineNumberFor(options, i) { - return String(options.lineNumberFormatter(i + options.firstLineNumber)) -} diff --git a/CODE/js/codemirror/src/measurement/position_measurement.js b/CODE/js/codemirror/src/measurement/position_measurement.js deleted file mode 100644 index bb0ad50d..00000000 --- a/CODE/js/codemirror/src/measurement/position_measurement.js +++ /dev/null @@ -1,700 +0,0 @@ -import { buildLineContent, LineView } from "../line/line_data.js" -import { clipPos, Pos } from "../line/pos.js" -import { collapsedSpanAround, heightAtLine, lineIsHidden, visualLine } from "../line/spans.js" -import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line.js" -import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi.js" -import { chrome, android, ie, ie_version } from "../util/browser.js" -import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom.js" -import { e_target } from "../util/event.js" -import { hasBadZoomedRects } from "../util/feature_detection.js" -import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc.js" -import { updateLineForChanges } from "../display/update_line.js" - -import { widgetHeight } from "./widgets.js" - -// POSITION MEASUREMENT - -export function paddingTop(display) {return display.lineSpace.offsetTop} -export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} -export function paddingH(display) { - if (display.cachedPaddingH) return display.cachedPaddingH - let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")) - let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle - let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} - if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data - return data -} - -export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } -export function displayWidth(cm) { - return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth -} -export function displayHeight(cm) { - return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight -} - -// Ensure the lineView.wrapping.heights array is populated. This is -// an array of bottom offsets for the lines that make up a drawn -// line. When lineWrapping is on, there might be more than one -// height. -function ensureLineHeights(cm, lineView, rect) { - let wrapping = cm.options.lineWrapping - let curWidth = wrapping && displayWidth(cm) - if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { - let heights = lineView.measure.heights = [] - if (wrapping) { - lineView.measure.width = curWidth - let rects = lineView.text.firstChild.getClientRects() - for (let i = 0; i < rects.length - 1; i++) { - let cur = rects[i], next = rects[i + 1] - if (Math.abs(cur.bottom - next.bottom) > 2) - heights.push((cur.bottom + next.top) / 2 - rect.top) - } - } - heights.push(rect.bottom - rect.top) - } -} - -// Find a line map (mapping character offsets to text nodes) and a -// measurement cache for the given line number. (A line view might -// contain multiple lines when collapsed ranges are present.) -export function mapFromLineView(lineView, line, lineN) { - if (lineView.line == line) - return {map: lineView.measure.map, cache: lineView.measure.cache} - for (let i = 0; i < lineView.rest.length; i++) - if (lineView.rest[i] == line) - return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} - for (let i = 0; i < lineView.rest.length; i++) - if (lineNo(lineView.rest[i]) > lineN) - return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true} -} - -// Render a line into the hidden node display.externalMeasured. Used -// when measurement is needed for a line that's not in the viewport. -function updateExternalMeasurement(cm, line) { - line = visualLine(line) - let lineN = lineNo(line) - let view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) - view.lineN = lineN - let built = view.built = buildLineContent(cm, view) - view.text = built.pre - removeChildrenAndAdd(cm.display.lineMeasure, built.pre) - return view -} - -// Get a {top, bottom, left, right} box (in line-local coordinates) -// for a given character. -export function measureChar(cm, line, ch, bias) { - return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) -} - -// Find a line view that corresponds to the given line number. -export function findViewForLine(cm, lineN) { - if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) - return cm.display.view[findViewIndex(cm, lineN)] - let ext = cm.display.externalMeasured - if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) - return ext -} - -// Measurement can be split in two steps, the set-up work that -// applies to the whole line, and the measurement of the actual -// character. Functions like coordsChar, that need to do a lot of -// measurements in a row, can thus ensure that the set-up work is -// only done once. -export function prepareMeasureForLine(cm, line) { - let lineN = lineNo(line) - let view = findViewForLine(cm, lineN) - if (view && !view.text) { - view = null - } else if (view && view.changes) { - updateLineForChanges(cm, view, lineN, getDimensions(cm)) - cm.curOp.forceUpdate = true - } - if (!view) - view = updateExternalMeasurement(cm, line) - - let info = mapFromLineView(view, line, lineN) - return { - line: line, view: view, rect: null, - map: info.map, cache: info.cache, before: info.before, - hasHeights: false - } -} - -// Given a prepared measurement object, measures the position of an -// actual character (or fetches it from the cache). -export function measureCharPrepared(cm, prepared, ch, bias, varHeight) { - if (prepared.before) ch = -1 - let key = ch + (bias || ""), found - if (prepared.cache.hasOwnProperty(key)) { - found = prepared.cache[key] - } else { - if (!prepared.rect) - prepared.rect = prepared.view.text.getBoundingClientRect() - if (!prepared.hasHeights) { - ensureLineHeights(cm, prepared.view, prepared.rect) - prepared.hasHeights = true - } - found = measureCharInner(cm, prepared, ch, bias) - if (!found.bogus) prepared.cache[key] = found - } - return {left: found.left, right: found.right, - top: varHeight ? found.rtop : found.top, - bottom: varHeight ? found.rbottom : found.bottom} -} - -let nullRect = {left: 0, right: 0, top: 0, bottom: 0} - -export function nodeAndOffsetInLineMap(map, ch, bias) { - let node, start, end, collapse, mStart, mEnd - // First, search the line map for the text node corresponding to, - // or closest to, the target character. - for (let i = 0; i < map.length; i += 3) { - mStart = map[i] - mEnd = map[i + 1] - if (ch < mStart) { - start = 0; end = 1 - collapse = "left" - } else if (ch < mEnd) { - start = ch - mStart - end = start + 1 - } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { - end = mEnd - mStart - start = end - 1 - if (ch >= mEnd) collapse = "right" - } - if (start != null) { - node = map[i + 2] - if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) - collapse = bias - if (bias == "left" && start == 0) - while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { - node = map[(i -= 3) + 2] - collapse = "left" - } - if (bias == "right" && start == mEnd - mStart) - while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { - node = map[(i += 3) + 2] - collapse = "right" - } - break - } - } - return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} -} - -function getUsefulRect(rects, bias) { - let rect = nullRect - if (bias == "left") for (let i = 0; i < rects.length; i++) { - if ((rect = rects[i]).left != rect.right) break - } else for (let i = rects.length - 1; i >= 0; i--) { - if ((rect = rects[i]).left != rect.right) break - } - return rect -} - -function measureCharInner(cm, prepared, ch, bias) { - let place = nodeAndOffsetInLineMap(prepared.map, ch, bias) - let node = place.node, start = place.start, end = place.end, collapse = place.collapse - - let rect - if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. - for (let i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned - while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start - while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end - if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) - rect = node.parentNode.getBoundingClientRect() - else - rect = getUsefulRect(range(node, start, end).getClientRects(), bias) - if (rect.left || rect.right || start == 0) break - end = start - start = start - 1 - collapse = "right" - } - if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect) - } else { // If it is a widget, simply get the box for the whole widget. - if (start > 0) collapse = bias = "right" - let rects - if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) - rect = rects[bias == "right" ? rects.length - 1 : 0] - else - rect = node.getBoundingClientRect() - } - if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { - let rSpan = node.parentNode.getClientRects()[0] - if (rSpan) - rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} - else - rect = nullRect - } - - let rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top - let mid = (rtop + rbot) / 2 - let heights = prepared.view.measure.heights - let i = 0 - for (; i < heights.length - 1; i++) - if (mid < heights[i]) break - let top = i ? heights[i - 1] : 0, bot = heights[i] - let result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, - right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, - top: top, bottom: bot} - if (!rect.left && !rect.right) result.bogus = true - if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot } - - return result -} - -// Work around problem with bounding client rects on ranges being -// returned incorrectly when zoomed on IE10 and below. -function maybeUpdateRectForZooming(measure, rect) { - if (!window.screen || screen.logicalXDPI == null || - screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) - return rect - let scaleX = screen.logicalXDPI / screen.deviceXDPI - let scaleY = screen.logicalYDPI / screen.deviceYDPI - return {left: rect.left * scaleX, right: rect.right * scaleX, - top: rect.top * scaleY, bottom: rect.bottom * scaleY} -} - -export function clearLineMeasurementCacheFor(lineView) { - if (lineView.measure) { - lineView.measure.cache = {} - lineView.measure.heights = null - if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) - lineView.measure.caches[i] = {} - } -} - -export function clearLineMeasurementCache(cm) { - cm.display.externalMeasure = null - removeChildren(cm.display.lineMeasure) - for (let i = 0; i < cm.display.view.length; i++) - clearLineMeasurementCacheFor(cm.display.view[i]) -} - -export function clearCaches(cm) { - clearLineMeasurementCache(cm) - cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null - if (!cm.options.lineWrapping) cm.display.maxLineChanged = true - cm.display.lineNumChars = null -} - -function pageScrollX() { - // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 - // which causes page_Offset and bounding client rects to use - // different reference viewports and invalidate our calculations. - if (chrome && android) return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) - return window.pageXOffset || (document.documentElement || document.body).scrollLeft -} -function pageScrollY() { - if (chrome && android) return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) - return window.pageYOffset || (document.documentElement || document.body).scrollTop -} - -function widgetTopHeight(lineObj) { - let height = 0 - if (lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) - height += widgetHeight(lineObj.widgets[i]) - return height -} - -// Converts a {top, bottom, left, right} box from line-local -// coordinates into another coordinate system. Context may be one of -// "line", "div" (display.lineDiv), "local"./null (editor), "window", -// or "page". -export function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { - if (!includeWidgets) { - let height = widgetTopHeight(lineObj) - rect.top += height; rect.bottom += height - } - if (context == "line") return rect - if (!context) context = "local" - let yOff = heightAtLine(lineObj) - if (context == "local") yOff += paddingTop(cm.display) - else yOff -= cm.display.viewOffset - if (context == "page" || context == "window") { - let lOff = cm.display.lineSpace.getBoundingClientRect() - yOff += lOff.top + (context == "window" ? 0 : pageScrollY()) - let xOff = lOff.left + (context == "window" ? 0 : pageScrollX()) - rect.left += xOff; rect.right += xOff - } - rect.top += yOff; rect.bottom += yOff - return rect -} - -// Coverts a box from "div" coords to another coordinate system. -// Context may be "window", "page", "div", or "local"./null. -export function fromCoordSystem(cm, coords, context) { - if (context == "div") return coords - let left = coords.left, top = coords.top - // First move into "page" coordinate system - if (context == "page") { - left -= pageScrollX() - top -= pageScrollY() - } else if (context == "local" || !context) { - let localBox = cm.display.sizer.getBoundingClientRect() - left += localBox.left - top += localBox.top - } - - let lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() - return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} -} - -export function charCoords(cm, pos, context, lineObj, bias) { - if (!lineObj) lineObj = getLine(cm.doc, pos.line) - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) -} - -// Returns a box for a given cursor position, which may have an -// 'other' property containing the position of the secondary cursor -// on a bidi boundary. -// A cursor Pos(line, char, "before") is on the same visual line as `char - 1` -// and after `char - 1` in writing order of `char - 1` -// A cursor Pos(line, char, "after") is on the same visual line as `char` -// and before `char` in writing order of `char` -// Examples (upper-case letters are RTL, lower-case are LTR): -// Pos(0, 1, ...) -// before after -// ab a|b a|b -// aB a|B aB| -// Ab |Ab A|b -// AB B|A B|A -// Every position after the last character on a line is considered to stick -// to the last character on the line. -export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { - lineObj = lineObj || getLine(cm.doc, pos.line) - if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) - function get(ch, right) { - let m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight) - if (right) m.left = m.right; else m.right = m.left - return intoCoordSystem(cm, lineObj, m, context) - } - let order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky - if (ch >= lineObj.text.length) { - ch = lineObj.text.length - sticky = "before" - } else if (ch <= 0) { - ch = 0 - sticky = "after" - } - if (!order) return get(sticky == "before" ? ch - 1 : ch, sticky == "before") - - function getBidi(ch, partPos, invert) { - let part = order[partPos], right = part.level == 1 - return get(invert ? ch - 1 : ch, right != invert) - } - let partPos = getBidiPartAt(order, ch, sticky) - let other = bidiOther - let val = getBidi(ch, partPos, sticky == "before") - if (other != null) val.other = getBidi(ch, other, sticky != "before") - return val -} - -// Used to cheaply estimate the coordinates for a position. Used for -// intermediate scroll updates. -export function estimateCoords(cm, pos) { - let left = 0 - pos = clipPos(cm.doc, pos) - if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch - let lineObj = getLine(cm.doc, pos.line) - let top = heightAtLine(lineObj) + paddingTop(cm.display) - return {left: left, right: left, top: top, bottom: top + lineObj.height} -} - -// Positions returned by coordsChar contain some extra information. -// xRel is the relative x position of the input coordinates compared -// to the found position (so xRel > 0 means the coordinates are to -// the right of the character position, for example). When outside -// is true, that means the coordinates lie outside the line's -// vertical range. -function PosWithInfo(line, ch, sticky, outside, xRel) { - let pos = Pos(line, ch, sticky) - pos.xRel = xRel - if (outside) pos.outside = outside - return pos -} - -// Compute the character position closest to the given coordinates. -// Input must be lineSpace-local ("div" coordinate system). -export function coordsChar(cm, x, y) { - let doc = cm.doc - y += cm.display.viewOffset - if (y < 0) return PosWithInfo(doc.first, 0, null, -1, -1) - let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 - if (lineN > last) - return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) - if (x < 0) x = 0 - - let lineObj = getLine(doc, lineN) - for (;;) { - let found = coordsCharInner(cm, lineObj, lineN, x, y) - let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)) - if (!collapsed) return found - let rangeEnd = collapsed.find(1) - if (rangeEnd.line == lineN) return rangeEnd - lineObj = getLine(doc, lineN = rangeEnd.line) - } -} - -function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { - y -= widgetTopHeight(lineObj) - let end = lineObj.text.length - let begin = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y, end, 0) - end = findFirst(ch => measureCharPrepared(cm, preparedMeasure, ch).top > y, begin, end) - return {begin, end} -} - -export function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { - if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) - let targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top - return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) -} - -// Returns true if the given side of a box is after the given -// coordinates, in top-to-bottom, left-to-right order. -function boxIsAfter(box, x, y, left) { - return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x -} - -function coordsCharInner(cm, lineObj, lineNo, x, y) { - // Move y into line-local coordinate space - y -= heightAtLine(lineObj) - let preparedMeasure = prepareMeasureForLine(cm, lineObj) - // When directly calling `measureCharPrepared`, we have to adjust - // for the widgets at this line. - let widgetHeight = widgetTopHeight(lineObj) - let begin = 0, end = lineObj.text.length, ltr = true - - let order = getOrder(lineObj, cm.doc.direction) - // If the line isn't plain left-to-right text, first figure out - // which bidi section the coordinates fall into. - if (order) { - let part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) - (cm, lineObj, lineNo, preparedMeasure, order, x, y) - ltr = part.level != 1 - // The awkward -1 offsets are needed because findFirst (called - // on these below) will treat its first bound as inclusive, - // second as exclusive, but we want to actually address the - // characters in the part's range - begin = ltr ? part.from : part.to - 1 - end = ltr ? part.to : part.from - 1 - } - - // A binary search to find the first character whose bounding box - // starts after the coordinates. If we run across any whose box wrap - // the coordinates, store that. - let chAround = null, boxAround = null - let ch = findFirst(ch => { - let box = measureCharPrepared(cm, preparedMeasure, ch) - box.top += widgetHeight; box.bottom += widgetHeight - if (!boxIsAfter(box, x, y, false)) return false - if (box.top <= y && box.left <= x) { - chAround = ch - boxAround = box - } - return true - }, begin, end) - - let baseX, sticky, outside = false - // If a box around the coordinates was found, use that - if (boxAround) { - // Distinguish coordinates nearer to the left or right side of the box - let atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr - ch = chAround + (atStart ? 0 : 1) - sticky = atStart ? "after" : "before" - baseX = atLeft ? boxAround.left : boxAround.right - } else { - // (Adjust for extended bound, if necessary.) - if (!ltr && (ch == end || ch == begin)) ch++ - // To determine which side to associate with, get the box to the - // left of the character and compare it's vertical position to the - // coordinates - sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : - (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? - "after" : "before" - // Now get accurate coordinates for this place, in order to get a - // base X position - let coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure) - baseX = coords.left - outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0 - } - - ch = skipExtendingChars(lineObj.text, ch, 1) - return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) -} - -function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { - // Bidi parts are sorted left-to-right, and in a non-line-wrapping - // situation, we can take this ordering to correspond to the visual - // ordering. This finds the first part whose end is after the given - // coordinates. - let index = findFirst(i => { - let part = order[i], ltr = part.level != 1 - return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), - "line", lineObj, preparedMeasure), x, y, true) - }, 0, order.length - 1) - let part = order[index] - // If this isn't the first part, the part's start is also after - // the coordinates, and the coordinates aren't on the same line as - // that start, move one part back. - if (index > 0) { - let ltr = part.level != 1 - let start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), - "line", lineObj, preparedMeasure) - if (boxIsAfter(start, x, y, true) && start.top > y) - part = order[index - 1] - } - return part -} - -function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { - // In a wrapped line, rtl text on wrapping boundaries can do things - // that don't correspond to the ordering in our `order` array at - // all, so a binary search doesn't work, and we want to return a - // part that only spans one line so that the binary search in - // coordsCharInner is safe. As such, we first find the extent of the - // wrapped line, and then do a flat search in which we discard any - // spans that aren't on the line. - let {begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y) - if (/\s/.test(lineObj.text.charAt(end - 1))) end-- - let part = null, closestDist = null - for (let i = 0; i < order.length; i++) { - let p = order[i] - if (p.from >= end || p.to <= begin) continue - let ltr = p.level != 1 - let endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right - // Weigh against spans ending before this, so that they are only - // picked if nothing ends after - let dist = endX < x ? x - endX + 1e9 : endX - x - if (!part || closestDist > dist) { - part = p - closestDist = dist - } - } - if (!part) part = order[order.length - 1] - // Clip the part to the wrapped line. - if (part.from < begin) part = {from: begin, to: part.to, level: part.level} - if (part.to > end) part = {from: part.from, to: end, level: part.level} - return part -} - -let measureText -// Compute the default text height. -export function textHeight(display) { - if (display.cachedTextHeight != null) return display.cachedTextHeight - if (measureText == null) { - measureText = elt("pre", null, "CodeMirror-line-like") - // Measure a bunch of lines, for browsers that compute - // fractional heights. - for (let i = 0; i < 49; ++i) { - measureText.appendChild(document.createTextNode("x")) - measureText.appendChild(elt("br")) - } - measureText.appendChild(document.createTextNode("x")) - } - removeChildrenAndAdd(display.measure, measureText) - let height = measureText.offsetHeight / 50 - if (height > 3) display.cachedTextHeight = height - removeChildren(display.measure) - return height || 1 -} - -// Compute the default character width. -export function charWidth(display) { - if (display.cachedCharWidth != null) return display.cachedCharWidth - let anchor = elt("span", "xxxxxxxxxx") - let pre = elt("pre", [anchor], "CodeMirror-line-like") - removeChildrenAndAdd(display.measure, pre) - let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 - if (width > 2) display.cachedCharWidth = width - return width || 10 -} - -// Do a bulk-read of the DOM positions and sizes needed to draw the -// view, so that we don't interleave reading and writing to the DOM. -export function getDimensions(cm) { - let d = cm.display, left = {}, width = {} - let gutterLeft = d.gutters.clientLeft - for (let n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { - let id = cm.display.gutterSpecs[i].className - left[id] = n.offsetLeft + n.clientLeft + gutterLeft - width[id] = n.clientWidth - } - return {fixedPos: compensateForHScroll(d), - gutterTotalWidth: d.gutters.offsetWidth, - gutterLeft: left, - gutterWidth: width, - wrapperWidth: d.wrapper.clientWidth} -} - -// Computes display.scroller.scrollLeft + display.gutters.offsetWidth, -// but using getBoundingClientRect to get a sub-pixel-accurate -// result. -export function compensateForHScroll(display) { - return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left -} - -// Returns a function that estimates the height of a line, to use as -// first approximation until the line becomes visible (and is thus -// properly measurable). -export function estimateHeight(cm) { - let th = textHeight(cm.display), wrapping = cm.options.lineWrapping - let perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3) - return line => { - if (lineIsHidden(cm.doc, line)) return 0 - - let widgetsHeight = 0 - if (line.widgets) for (let i = 0; i < line.widgets.length; i++) { - if (line.widgets[i].height) widgetsHeight += line.widgets[i].height - } - - if (wrapping) - return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th - else - return widgetsHeight + th - } -} - -export function estimateLineHeights(cm) { - let doc = cm.doc, est = estimateHeight(cm) - doc.iter(line => { - let estHeight = est(line) - if (estHeight != line.height) updateLineHeight(line, estHeight) - }) -} - -// Given a mouse event, find the corresponding position. If liberal -// is false, it checks whether a gutter or scrollbar was clicked, -// and returns null if it was. forRect is used by rectangular -// selections, and tries to estimate a character position even for -// coordinates beyond the right of the text. -export function posFromMouse(cm, e, liberal, forRect) { - let display = cm.display - if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null - - let x, y, space = display.lineSpace.getBoundingClientRect() - // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX - space.left; y = e.clientY - space.top } - catch (e) { return null } - let coords = coordsChar(cm, x, y), line - if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { - let colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length - coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)) - } - return coords -} - -// Find the view element corresponding to a given line. Return null -// when the line isn't visible. -export function findViewIndex(cm, n) { - if (n >= cm.display.viewTo) return null - n -= cm.display.viewFrom - if (n < 0) return null - let view = cm.display.view - for (let i = 0; i < view.length; i++) { - n -= view[i].size - if (n < 0) return i - } -} diff --git a/CODE/js/codemirror/src/measurement/widgets.js b/CODE/js/codemirror/src/measurement/widgets.js deleted file mode 100644 index 39d7553d..00000000 --- a/CODE/js/codemirror/src/measurement/widgets.js +++ /dev/null @@ -1,26 +0,0 @@ -import { contains, elt, removeChildrenAndAdd } from "../util/dom.js" -import { e_target } from "../util/event.js" - -export function widgetHeight(widget) { - if (widget.height != null) return widget.height - let cm = widget.doc.cm - if (!cm) return 0 - if (!contains(document.body, widget.node)) { - let parentStyle = "position: relative;" - if (widget.coverGutter) - parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" - if (widget.noHScroll) - parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" - removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)) - } - return widget.height = widget.node.parentNode.offsetHeight -} - -// Return true when the given mouse event happened in a widget -export function eventInWidget(display, e) { - for (let n = e_target(e); n != display.wrapper; n = n.parentNode) { - if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || - (n.parentNode == display.sizer && n != display.mover)) - return true - } -} diff --git a/CODE/js/codemirror/src/model/Doc.js b/CODE/js/codemirror/src/model/Doc.js deleted file mode 100644 index c305eee2..00000000 --- a/CODE/js/codemirror/src/model/Doc.js +++ /dev/null @@ -1,435 +0,0 @@ -import CodeMirror from "../edit/CodeMirror.js" -import { docMethodOp } from "../display/operations.js" -import { Line } from "../line/line_data.js" -import { clipPos, clipPosArray, Pos } from "../line/pos.js" -import { visualLine } from "../line/spans.js" -import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js" -import { classTest } from "../util/dom.js" -import { splitLinesAuto } from "../util/feature_detection.js" -import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js" -import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js" - -import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js" -import { computeReplacedSel } from "./change_measurement.js" -import { BranchChunk, LeafChunk } from "./chunk.js" -import { directionChanged, linkedDocs, updateDoc } from "./document_data.js" -import { copyHistoryArray, History } from "./history.js" -import { addLineWidget } from "./line_widget.js" -import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js" -import { normalizeSelection, Range, simpleSelection } from "./selection.js" -import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js" - -let nextDocId = 0 -let Doc = function(text, mode, firstLine, lineSep, direction) { - if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep, direction) - if (firstLine == null) firstLine = 0 - - BranchChunk.call(this, [new LeafChunk([new Line("", null)])]) - this.first = firstLine - this.scrollTop = this.scrollLeft = 0 - this.cantEdit = false - this.cleanGeneration = 1 - this.modeFrontier = this.highlightFrontier = firstLine - let start = Pos(firstLine, 0) - this.sel = simpleSelection(start) - this.history = new History(null) - this.id = ++nextDocId - this.modeOption = mode - this.lineSep = lineSep - this.direction = (direction == "rtl") ? "rtl" : "ltr" - this.extend = false - - if (typeof text == "string") text = this.splitLines(text) - updateDoc(this, {from: start, to: start, text: text}) - setSelection(this, simpleSelection(start), sel_dontScroll) -} - -Doc.prototype = createObj(BranchChunk.prototype, { - constructor: Doc, - // Iterate over the document. Supports two forms -- with only one - // argument, it calls that for each line in the document. With - // three, it iterates over the range given by the first two (with - // the second being non-inclusive). - iter: function(from, to, op) { - if (op) this.iterN(from - this.first, to - from, op) - else this.iterN(this.first, this.first + this.size, from) - }, - - // Non-public interface for adding and removing lines. - insert: function(at, lines) { - let height = 0 - for (let i = 0; i < lines.length; ++i) height += lines[i].height - this.insertInner(at - this.first, lines, height) - }, - remove: function(at, n) { this.removeInner(at - this.first, n) }, - - // From here, the methods are part of the public interface. Most - // are also available from CodeMirror (editor) instances. - - getValue: function(lineSep) { - let lines = getLines(this, this.first, this.first + this.size) - if (lineSep === false) return lines - return lines.join(lineSep || this.lineSeparator()) - }, - setValue: docMethodOp(function(code) { - let top = Pos(this.first, 0), last = this.first + this.size - 1 - makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), - text: this.splitLines(code), origin: "setValue", full: true}, true) - if (this.cm) scrollToCoords(this.cm, 0, 0) - setSelection(this, simpleSelection(top), sel_dontScroll) - }), - replaceRange: function(code, from, to, origin) { - from = clipPos(this, from) - to = to ? clipPos(this, to) : from - replaceRange(this, code, from, to, origin) - }, - getRange: function(from, to, lineSep) { - let lines = getBetween(this, clipPos(this, from), clipPos(this, to)) - if (lineSep === false) return lines - return lines.join(lineSep || this.lineSeparator()) - }, - - getLine: function(line) {let l = this.getLineHandle(line); return l && l.text}, - - getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)}, - getLineNumber: function(line) {return lineNo(line)}, - - getLineHandleVisualStart: function(line) { - if (typeof line == "number") line = getLine(this, line) - return visualLine(line) - }, - - lineCount: function() {return this.size}, - firstLine: function() {return this.first}, - lastLine: function() {return this.first + this.size - 1}, - - clipPos: function(pos) {return clipPos(this, pos)}, - - getCursor: function(start) { - let range = this.sel.primary(), pos - if (start == null || start == "head") pos = range.head - else if (start == "anchor") pos = range.anchor - else if (start == "end" || start == "to" || start === false) pos = range.to() - else pos = range.from() - return pos - }, - listSelections: function() { return this.sel.ranges }, - somethingSelected: function() {return this.sel.somethingSelected()}, - - setCursor: docMethodOp(function(line, ch, options) { - setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options) - }), - setSelection: docMethodOp(function(anchor, head, options) { - setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options) - }), - extendSelection: docMethodOp(function(head, other, options) { - extendSelection(this, clipPos(this, head), other && clipPos(this, other), options) - }), - extendSelections: docMethodOp(function(heads, options) { - extendSelections(this, clipPosArray(this, heads), options) - }), - extendSelectionsBy: docMethodOp(function(f, options) { - let heads = map(this.sel.ranges, f) - extendSelections(this, clipPosArray(this, heads), options) - }), - setSelections: docMethodOp(function(ranges, primary, options) { - if (!ranges.length) return - let out = [] - for (let i = 0; i < ranges.length; i++) - out[i] = new Range(clipPos(this, ranges[i].anchor), - clipPos(this, ranges[i].head)) - if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex) - setSelection(this, normalizeSelection(this.cm, out, primary), options) - }), - addSelection: docMethodOp(function(anchor, head, options) { - let ranges = this.sel.ranges.slice(0) - ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) - setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options) - }), - - getSelection: function(lineSep) { - let ranges = this.sel.ranges, lines - for (let i = 0; i < ranges.length; i++) { - let sel = getBetween(this, ranges[i].from(), ranges[i].to()) - lines = lines ? lines.concat(sel) : sel - } - if (lineSep === false) return lines - else return lines.join(lineSep || this.lineSeparator()) - }, - getSelections: function(lineSep) { - let parts = [], ranges = this.sel.ranges - for (let i = 0; i < ranges.length; i++) { - let sel = getBetween(this, ranges[i].from(), ranges[i].to()) - if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()) - parts[i] = sel - } - return parts - }, - replaceSelection: function(code, collapse, origin) { - let dup = [] - for (let i = 0; i < this.sel.ranges.length; i++) - dup[i] = code - this.replaceSelections(dup, collapse, origin || "+input") - }, - replaceSelections: docMethodOp(function(code, collapse, origin) { - let changes = [], sel = this.sel - for (let i = 0; i < sel.ranges.length; i++) { - let range = sel.ranges[i] - changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin} - } - let newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) - for (let i = changes.length - 1; i >= 0; i--) - makeChange(this, changes[i]) - if (newSel) setSelectionReplaceHistory(this, newSel) - else if (this.cm) ensureCursorVisible(this.cm) - }), - undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}), - redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}), - undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}), - redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}), - - setExtending: function(val) {this.extend = val}, - getExtending: function() {return this.extend}, - - historySize: function() { - let hist = this.history, done = 0, undone = 0 - for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done - for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone - return {undo: done, redo: undone} - }, - clearHistory: function() { - this.history = new History(this.history.maxGeneration) - linkedDocs(this, doc => doc.history = this.history, true) - }, - - markClean: function() { - this.cleanGeneration = this.changeGeneration(true) - }, - changeGeneration: function(forceSplit) { - if (forceSplit) - this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null - return this.history.generation - }, - isClean: function (gen) { - return this.history.generation == (gen || this.cleanGeneration) - }, - - getHistory: function() { - return {done: copyHistoryArray(this.history.done), - undone: copyHistoryArray(this.history.undone)} - }, - setHistory: function(histData) { - let hist = this.history = new History(this.history.maxGeneration) - hist.done = copyHistoryArray(histData.done.slice(0), null, true) - hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) - }, - - setGutterMarker: docMethodOp(function(line, gutterID, value) { - return changeLine(this, line, "gutter", line => { - let markers = line.gutterMarkers || (line.gutterMarkers = {}) - markers[gutterID] = value - if (!value && isEmpty(markers)) line.gutterMarkers = null - return true - }) - }), - - clearGutter: docMethodOp(function(gutterID) { - this.iter(line => { - if (line.gutterMarkers && line.gutterMarkers[gutterID]) { - changeLine(this, line, "gutter", () => { - line.gutterMarkers[gutterID] = null - if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null - return true - }) - } - }) - }), - - lineInfo: function(line) { - let n - if (typeof line == "number") { - if (!isLine(this, line)) return null - n = line - line = getLine(this, line) - if (!line) return null - } else { - n = lineNo(line) - if (n == null) return null - } - return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, - textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, - widgets: line.widgets} - }, - - addLineClass: docMethodOp(function(handle, where, cls) { - return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { - let prop = where == "text" ? "textClass" - : where == "background" ? "bgClass" - : where == "gutter" ? "gutterClass" : "wrapClass" - if (!line[prop]) line[prop] = cls - else if (classTest(cls).test(line[prop])) return false - else line[prop] += " " + cls - return true - }) - }), - removeLineClass: docMethodOp(function(handle, where, cls) { - return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { - let prop = where == "text" ? "textClass" - : where == "background" ? "bgClass" - : where == "gutter" ? "gutterClass" : "wrapClass" - let cur = line[prop] - if (!cur) return false - else if (cls == null) line[prop] = null - else { - let found = cur.match(classTest(cls)) - if (!found) return false - let end = found.index + found[0].length - line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null - } - return true - }) - }), - - addLineWidget: docMethodOp(function(handle, node, options) { - return addLineWidget(this, handle, node, options) - }), - removeLineWidget: function(widget) { widget.clear() }, - - markText: function(from, to, options) { - return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") - }, - setBookmark: function(pos, options) { - let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), - insertLeft: options && options.insertLeft, - clearWhenEmpty: false, shared: options && options.shared, - handleMouseEvents: options && options.handleMouseEvents} - pos = clipPos(this, pos) - return markText(this, pos, pos, realOpts, "bookmark") - }, - findMarksAt: function(pos) { - pos = clipPos(this, pos) - let markers = [], spans = getLine(this, pos.line).markedSpans - if (spans) for (let i = 0; i < spans.length; ++i) { - let span = spans[i] - if ((span.from == null || span.from <= pos.ch) && - (span.to == null || span.to >= pos.ch)) - markers.push(span.marker.parent || span.marker) - } - return markers - }, - findMarks: function(from, to, filter) { - from = clipPos(this, from); to = clipPos(this, to) - let found = [], lineNo = from.line - this.iter(from.line, to.line + 1, line => { - let spans = line.markedSpans - if (spans) for (let i = 0; i < spans.length; i++) { - let span = spans[i] - if (!(span.to != null && lineNo == from.line && from.ch >= span.to || - span.from == null && lineNo != from.line || - span.from != null && lineNo == to.line && span.from >= to.ch) && - (!filter || filter(span.marker))) - found.push(span.marker.parent || span.marker) - } - ++lineNo - }) - return found - }, - getAllMarks: function() { - let markers = [] - this.iter(line => { - let sps = line.markedSpans - if (sps) for (let i = 0; i < sps.length; ++i) - if (sps[i].from != null) markers.push(sps[i].marker) - }) - return markers - }, - - posFromIndex: function(off) { - let ch, lineNo = this.first, sepSize = this.lineSeparator().length - this.iter(line => { - let sz = line.text.length + sepSize - if (sz > off) { ch = off; return true } - off -= sz - ++lineNo - }) - return clipPos(this, Pos(lineNo, ch)) - }, - indexFromPos: function (coords) { - coords = clipPos(this, coords) - let index = coords.ch - if (coords.line < this.first || coords.ch < 0) return 0 - let sepSize = this.lineSeparator().length - this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value - index += line.text.length + sepSize - }) - return index - }, - - copy: function(copyHistory) { - let doc = new Doc(getLines(this, this.first, this.first + this.size), - this.modeOption, this.first, this.lineSep, this.direction) - doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft - doc.sel = this.sel - doc.extend = false - if (copyHistory) { - doc.history.undoDepth = this.history.undoDepth - doc.setHistory(this.getHistory()) - } - return doc - }, - - linkedDoc: function(options) { - if (!options) options = {} - let from = this.first, to = this.first + this.size - if (options.from != null && options.from > from) from = options.from - if (options.to != null && options.to < to) to = options.to - let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction) - if (options.sharedHist) copy.history = this.history - ;(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}) - copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}] - copySharedMarkers(copy, findSharedMarkers(this)) - return copy - }, - unlinkDoc: function(other) { - if (other instanceof CodeMirror) other = other.doc - if (this.linked) for (let i = 0; i < this.linked.length; ++i) { - let link = this.linked[i] - if (link.doc != other) continue - this.linked.splice(i, 1) - other.unlinkDoc(this) - detachSharedMarkers(findSharedMarkers(this)) - break - } - // If the histories were shared, split them again - if (other.history == this.history) { - let splitIds = [other.id] - linkedDocs(other, doc => splitIds.push(doc.id), true) - other.history = new History(null) - other.history.done = copyHistoryArray(this.history.done, splitIds) - other.history.undone = copyHistoryArray(this.history.undone, splitIds) - } - }, - iterLinkedDocs: function(f) {linkedDocs(this, f)}, - - getMode: function() {return this.mode}, - getEditor: function() {return this.cm}, - - splitLines: function(str) { - if (this.lineSep) return str.split(this.lineSep) - return splitLinesAuto(str) - }, - lineSeparator: function() { return this.lineSep || "\n" }, - - setDirection: docMethodOp(function (dir) { - if (dir != "rtl") dir = "ltr" - if (dir == this.direction) return - this.direction = dir - this.iter(line => line.order = null) - if (this.cm) directionChanged(this.cm) - }) -}) - -// Public alias. -Doc.prototype.eachLine = Doc.prototype.iter - -export default Doc diff --git a/CODE/js/codemirror/src/model/change_measurement.js b/CODE/js/codemirror/src/model/change_measurement.js deleted file mode 100644 index 010e7f81..00000000 --- a/CODE/js/codemirror/src/model/change_measurement.js +++ /dev/null @@ -1,61 +0,0 @@ -import { cmp, Pos } from "../line/pos.js" -import { lst } from "../util/misc.js" - -import { normalizeSelection, Range, Selection } from "./selection.js" - -// Compute the position of the end of a change (its 'to' property -// refers to the pre-change end). -export function changeEnd(change) { - if (!change.text) return change.to - return Pos(change.from.line + change.text.length - 1, - lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) -} - -// Adjust a position to refer to the post-change position of the -// same text, or the end of the change if the change covers it. -function adjustForChange(pos, change) { - if (cmp(pos, change.from) < 0) return pos - if (cmp(pos, change.to) <= 0) return changeEnd(change) - - let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch - if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch - return Pos(line, ch) -} - -export function computeSelAfterChange(doc, change) { - let out = [] - for (let i = 0; i < doc.sel.ranges.length; i++) { - let range = doc.sel.ranges[i] - out.push(new Range(adjustForChange(range.anchor, change), - adjustForChange(range.head, change))) - } - return normalizeSelection(doc.cm, out, doc.sel.primIndex) -} - -function offsetPos(pos, old, nw) { - if (pos.line == old.line) - return Pos(nw.line, pos.ch - old.ch + nw.ch) - else - return Pos(nw.line + (pos.line - old.line), pos.ch) -} - -// Used by replaceSelections to allow moving the selection to the -// start or around the replaced test. Hint may be "start" or "around". -export function computeReplacedSel(doc, changes, hint) { - let out = [] - let oldPrev = Pos(doc.first, 0), newPrev = oldPrev - for (let i = 0; i < changes.length; i++) { - let change = changes[i] - let from = offsetPos(change.from, oldPrev, newPrev) - let to = offsetPos(changeEnd(change), oldPrev, newPrev) - oldPrev = change.to - newPrev = to - if (hint == "around") { - let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 - out[i] = new Range(inv ? to : from, inv ? from : to) - } else { - out[i] = new Range(from, from) - } - } - return new Selection(out, doc.sel.primIndex) -} diff --git a/CODE/js/codemirror/src/model/changes.js b/CODE/js/codemirror/src/model/changes.js deleted file mode 100644 index 48d2f6bb..00000000 --- a/CODE/js/codemirror/src/model/changes.js +++ /dev/null @@ -1,339 +0,0 @@ -import { retreatFrontier } from "../line/highlight.js" -import { startWorker } from "../display/highlight_worker.js" -import { operation } from "../display/operations.js" -import { regChange, regLineChange } from "../display/view_tracking.js" -import { clipLine, clipPos, cmp, Pos } from "../line/pos.js" -import { sawReadOnlySpans } from "../line/saw_special_spans.js" -import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js" -import { getBetween, getLine, lineNo } from "../line/utils_line.js" -import { estimateHeight } from "../measurement/position_measurement.js" -import { hasHandler, signal, signalCursorActivity } from "../util/event.js" -import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" - -import { changeEnd, computeSelAfterChange } from "./change_measurement.js" -import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js" -import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js" -import { Range, Selection } from "./selection.js" -import { setSelection, setSelectionNoUndo, skipAtomic } from "./selection_updates.js" - -// UPDATING - -// Allow "beforeChange" event handlers to influence a change -function filterChange(doc, change, update) { - let obj = { - canceled: false, - from: change.from, - to: change.to, - text: change.text, - origin: change.origin, - cancel: () => obj.canceled = true - } - if (update) obj.update = (from, to, text, origin) => { - if (from) obj.from = clipPos(doc, from) - if (to) obj.to = clipPos(doc, to) - if (text) obj.text = text - if (origin !== undefined) obj.origin = origin - } - signal(doc, "beforeChange", doc, obj) - if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj) - - if (obj.canceled) { - if (doc.cm) doc.cm.curOp.updateInput = 2 - return null - } - return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} -} - -// Apply a change to a document, and add it to the document's -// history, and propagating it to all linked documents. -export function makeChange(doc, change, ignoreReadOnly) { - if (doc.cm) { - if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) - if (doc.cm.state.suppressEdits) return - } - - if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { - change = filterChange(doc, change, true) - if (!change) return - } - - // Possibly split or suppress the update based on the presence - // of read-only spans in its range. - let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) - if (split) { - for (let i = split.length - 1; i >= 0; --i) - makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) - } else { - makeChangeInner(doc, change) - } -} - -function makeChangeInner(doc, change) { - if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return - let selAfter = computeSelAfterChange(doc, change) - addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) - - makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) - let rebased = [] - - linkedDocs(doc, (doc, sharedHist) => { - if (!sharedHist && indexOf(rebased, doc.history) == -1) { - rebaseHist(doc.history, change) - rebased.push(doc.history) - } - makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)) - }) -} - -// Revert a change stored in a document's history. -export function makeChangeFromHistory(doc, type, allowSelectionOnly) { - let suppress = doc.cm && doc.cm.state.suppressEdits - if (suppress && !allowSelectionOnly) return - - let hist = doc.history, event, selAfter = doc.sel - let source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done - - // Verify that there is a useable event (so that ctrl-z won't - // needlessly clear selection events) - let i = 0 - for (; i < source.length; i++) { - event = source[i] - if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) - break - } - if (i == source.length) return - hist.lastOrigin = hist.lastSelOrigin = null - - for (;;) { - event = source.pop() - if (event.ranges) { - pushSelectionToHistory(event, dest) - if (allowSelectionOnly && !event.equals(doc.sel)) { - setSelection(doc, event, {clearRedo: false}) - return - } - selAfter = event - } else if (suppress) { - source.push(event) - return - } else break - } - - // Build up a reverse change object to add to the opposite history - // stack (redo when undoing, and vice versa). - let antiChanges = [] - pushSelectionToHistory(selAfter, dest) - dest.push({changes: antiChanges, generation: hist.generation}) - hist.generation = event.generation || ++hist.maxGeneration - - let filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange") - - for (let i = event.changes.length - 1; i >= 0; --i) { - let change = event.changes[i] - change.origin = type - if (filter && !filterChange(doc, change, false)) { - source.length = 0 - return - } - - antiChanges.push(historyChangeFromChange(doc, change)) - - let after = i ? computeSelAfterChange(doc, change) : lst(source) - makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) - if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) - let rebased = [] - - // Propagate to the linked documents - linkedDocs(doc, (doc, sharedHist) => { - if (!sharedHist && indexOf(rebased, doc.history) == -1) { - rebaseHist(doc.history, change) - rebased.push(doc.history) - } - makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)) - }) - } -} - -// Sub-views need their line numbers shifted when text is added -// above or below them in the parent document. -function shiftDoc(doc, distance) { - if (distance == 0) return - doc.first += distance - doc.sel = new Selection(map(doc.sel.ranges, range => new Range( - Pos(range.anchor.line + distance, range.anchor.ch), - Pos(range.head.line + distance, range.head.ch) - )), doc.sel.primIndex) - if (doc.cm) { - regChange(doc.cm, doc.first, doc.first - distance, distance) - for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) - regLineChange(doc.cm, l, "gutter") - } -} - -// More lower-level change function, handling only a single document -// (not linked ones). -function makeChangeSingleDoc(doc, change, selAfter, spans) { - if (doc.cm && !doc.cm.curOp) - return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) - - if (change.to.line < doc.first) { - shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)) - return - } - if (change.from.line > doc.lastLine()) return - - // Clip the change to the size of this doc - if (change.from.line < doc.first) { - let shift = change.text.length - 1 - (doc.first - change.from.line) - shiftDoc(doc, shift) - change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), - text: [lst(change.text)], origin: change.origin} - } - let last = doc.lastLine() - if (change.to.line > last) { - change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), - text: [change.text[0]], origin: change.origin} - } - - change.removed = getBetween(doc, change.from, change.to) - - if (!selAfter) selAfter = computeSelAfterChange(doc, change) - if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans) - else updateDoc(doc, change, spans) - setSelectionNoUndo(doc, selAfter, sel_dontScroll) - - if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) - doc.cantEdit = false -} - -// Handle the interaction of a change to a document with the editor -// that this document is part of. -function makeChangeSingleDocInEditor(cm, change, spans) { - let doc = cm.doc, display = cm.display, from = change.from, to = change.to - - let recomputeMaxLength = false, checkWidthStart = from.line - if (!cm.options.lineWrapping) { - checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) - doc.iter(checkWidthStart, to.line + 1, line => { - if (line == display.maxLine) { - recomputeMaxLength = true - return true - } - }) - } - - if (doc.sel.contains(change.from, change.to) > -1) - signalCursorActivity(cm) - - updateDoc(doc, change, spans, estimateHeight(cm)) - - if (!cm.options.lineWrapping) { - doc.iter(checkWidthStart, from.line + change.text.length, line => { - let len = lineLength(line) - if (len > display.maxLineLength) { - display.maxLine = line - display.maxLineLength = len - display.maxLineChanged = true - recomputeMaxLength = false - } - }) - if (recomputeMaxLength) cm.curOp.updateMaxLine = true - } - - retreatFrontier(doc, from.line) - startWorker(cm, 400) - - let lendiff = change.text.length - (to.line - from.line) - 1 - // Remember that these lines changed, for updating the display - if (change.full) - regChange(cm) - else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) - regLineChange(cm, from.line, "text") - else - regChange(cm, from.line, to.line + 1, lendiff) - - let changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") - if (changeHandler || changesHandler) { - let obj = { - from: from, to: to, - text: change.text, - removed: change.removed, - origin: change.origin - } - if (changeHandler) signalLater(cm, "change", cm, obj) - if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) - } - cm.display.selForContextMenu = null -} - -export function replaceRange(doc, code, from, to, origin) { - if (!to) to = from - if (cmp(to, from) < 0) [from, to] = [to, from] - if (typeof code == "string") code = doc.splitLines(code) - makeChange(doc, {from, to, text: code, origin}) -} - -// Rebasing/resetting history to deal with externally-sourced changes - -function rebaseHistSelSingle(pos, from, to, diff) { - if (to < pos.line) { - pos.line += diff - } else if (from < pos.line) { - pos.line = from - pos.ch = 0 - } -} - -// Tries to rebase an array of history events given a change in the -// document. If the change touches the same lines as the event, the -// event, and everything 'behind' it, is discarded. If the change is -// before the event, the event's positions are updated. Uses a -// copy-on-write scheme for the positions, to avoid having to -// reallocate them all on every rebase, but also avoid problems with -// shared position objects being unsafely updated. -function rebaseHistArray(array, from, to, diff) { - for (let i = 0; i < array.length; ++i) { - let sub = array[i], ok = true - if (sub.ranges) { - if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } - for (let j = 0; j < sub.ranges.length; j++) { - rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) - rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) - } - continue - } - for (let j = 0; j < sub.changes.length; ++j) { - let cur = sub.changes[j] - if (to < cur.from.line) { - cur.from = Pos(cur.from.line + diff, cur.from.ch) - cur.to = Pos(cur.to.line + diff, cur.to.ch) - } else if (from <= cur.to.line) { - ok = false - break - } - } - if (!ok) { - array.splice(0, i + 1) - i = 0 - } - } -} - -function rebaseHist(hist, change) { - let from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1 - rebaseHistArray(hist.done, from, to, diff) - rebaseHistArray(hist.undone, from, to, diff) -} - -// Utility for applying a change to a line by handle or number, -// returning the number and optionally registering the line as -// changed. -export function changeLine(doc, handle, changeType, op) { - let no = handle, line = handle - if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)) - else no = lineNo(handle) - if (no == null) return null - if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType) - return line -} diff --git a/CODE/js/codemirror/src/model/chunk.js b/CODE/js/codemirror/src/model/chunk.js deleted file mode 100644 index d82716de..00000000 --- a/CODE/js/codemirror/src/model/chunk.js +++ /dev/null @@ -1,167 +0,0 @@ -import { cleanUpLine } from "../line/line_data.js" -import { indexOf } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" - -// The document is represented as a BTree consisting of leaves, with -// chunk of lines in them, and branches, with up to ten leaves or -// other branch nodes below them. The top node is always a branch -// node, and is the document object itself (meaning it has -// additional methods and properties). -// -// All nodes have parent links. The tree is used both to go from -// line numbers to line objects, and to go from objects to numbers. -// It also indexes by height, and is used to convert between height -// and line object, and to find the total height of the document. -// -// See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html - -export function LeafChunk(lines) { - this.lines = lines - this.parent = null - let height = 0 - for (let i = 0; i < lines.length; ++i) { - lines[i].parent = this - height += lines[i].height - } - this.height = height -} - -LeafChunk.prototype = { - chunkSize() { return this.lines.length }, - - // Remove the n lines at offset 'at'. - removeInner(at, n) { - for (let i = at, e = at + n; i < e; ++i) { - let line = this.lines[i] - this.height -= line.height - cleanUpLine(line) - signalLater(line, "delete") - } - this.lines.splice(at, n) - }, - - // Helper used to collapse a small branch into a single leaf. - collapse(lines) { - lines.push.apply(lines, this.lines) - }, - - // Insert the given array of lines at offset 'at', count them as - // having the given height. - insertInner(at, lines, height) { - this.height += height - this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)) - for (let i = 0; i < lines.length; ++i) lines[i].parent = this - }, - - // Used to iterate over a part of the tree. - iterN(at, n, op) { - for (let e = at + n; at < e; ++at) - if (op(this.lines[at])) return true - } -} - -export function BranchChunk(children) { - this.children = children - let size = 0, height = 0 - for (let i = 0; i < children.length; ++i) { - let ch = children[i] - size += ch.chunkSize(); height += ch.height - ch.parent = this - } - this.size = size - this.height = height - this.parent = null -} - -BranchChunk.prototype = { - chunkSize() { return this.size }, - - removeInner(at, n) { - this.size -= n - for (let i = 0; i < this.children.length; ++i) { - let child = this.children[i], sz = child.chunkSize() - if (at < sz) { - let rm = Math.min(n, sz - at), oldHeight = child.height - child.removeInner(at, rm) - this.height -= oldHeight - child.height - if (sz == rm) { this.children.splice(i--, 1); child.parent = null } - if ((n -= rm) == 0) break - at = 0 - } else at -= sz - } - // If the result is smaller than 25 lines, ensure that it is a - // single leaf node. - if (this.size - n < 25 && - (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { - let lines = [] - this.collapse(lines) - this.children = [new LeafChunk(lines)] - this.children[0].parent = this - } - }, - - collapse(lines) { - for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) - }, - - insertInner(at, lines, height) { - this.size += lines.length - this.height += height - for (let i = 0; i < this.children.length; ++i) { - let child = this.children[i], sz = child.chunkSize() - if (at <= sz) { - child.insertInner(at, lines, height) - if (child.lines && child.lines.length > 50) { - // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. - // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. - let remaining = child.lines.length % 25 + 25 - for (let pos = remaining; pos < child.lines.length;) { - let leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) - child.height -= leaf.height - this.children.splice(++i, 0, leaf) - leaf.parent = this - } - child.lines = child.lines.slice(0, remaining) - this.maybeSpill() - } - break - } - at -= sz - } - }, - - // When a node has grown, check whether it should be split. - maybeSpill() { - if (this.children.length <= 10) return - let me = this - do { - let spilled = me.children.splice(me.children.length - 5, 5) - let sibling = new BranchChunk(spilled) - if (!me.parent) { // Become the parent node - let copy = new BranchChunk(me.children) - copy.parent = me - me.children = [copy, sibling] - me = copy - } else { - me.size -= sibling.size - me.height -= sibling.height - let myIndex = indexOf(me.parent.children, me) - me.parent.children.splice(myIndex + 1, 0, sibling) - } - sibling.parent = me.parent - } while (me.children.length > 10) - me.parent.maybeSpill() - }, - - iterN(at, n, op) { - for (let i = 0; i < this.children.length; ++i) { - let child = this.children[i], sz = child.chunkSize() - if (at < sz) { - let used = Math.min(n, sz - at) - if (child.iterN(at, used, op)) return true - if ((n -= used) == 0) break - at = 0 - } else at -= sz - } - } -} diff --git a/CODE/js/codemirror/src/model/document_data.js b/CODE/js/codemirror/src/model/document_data.js deleted file mode 100644 index d946e7af..00000000 --- a/CODE/js/codemirror/src/model/document_data.js +++ /dev/null @@ -1,111 +0,0 @@ -import { loadMode } from "../display/mode_state.js" -import { runInOp } from "../display/operations.js" -import { regChange } from "../display/view_tracking.js" -import { Line, updateLine } from "../line/line_data.js" -import { findMaxLine } from "../line/spans.js" -import { getLine } from "../line/utils_line.js" -import { estimateLineHeights } from "../measurement/position_measurement.js" -import { addClass, rmClass } from "../util/dom.js" -import { lst } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" - -// DOCUMENT DATA STRUCTURE - -// By default, updates that start and end at the beginning of a line -// are treated specially, in order to make the association of line -// widgets and marker elements with the text behave more intuitive. -export function isWholeLineUpdate(doc, change) { - return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && - (!doc.cm || doc.cm.options.wholeLineUpdateBefore) -} - -// Perform a change on the document data structure. -export function updateDoc(doc, change, markedSpans, estimateHeight) { - function spansFor(n) {return markedSpans ? markedSpans[n] : null} - function update(line, text, spans) { - updateLine(line, text, spans, estimateHeight) - signalLater(line, "change", line, change) - } - function linesFor(start, end) { - let result = [] - for (let i = start; i < end; ++i) - result.push(new Line(text[i], spansFor(i), estimateHeight)) - return result - } - - let from = change.from, to = change.to, text = change.text - let firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line) - let lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line - - // Adjust the line structure - if (change.full) { - doc.insert(0, linesFor(0, text.length)) - doc.remove(text.length, doc.size - text.length) - } else if (isWholeLineUpdate(doc, change)) { - // This is a whole-line replace. Treated specially to make - // sure line objects move the way they are supposed to. - let added = linesFor(0, text.length - 1) - update(lastLine, lastLine.text, lastSpans) - if (nlines) doc.remove(from.line, nlines) - if (added.length) doc.insert(from.line, added) - } else if (firstLine == lastLine) { - if (text.length == 1) { - update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans) - } else { - let added = linesFor(1, text.length - 1) - added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)) - update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) - doc.insert(from.line + 1, added) - } - } else if (text.length == 1) { - update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)) - doc.remove(from.line + 1, nlines) - } else { - update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) - update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans) - let added = linesFor(1, text.length - 1) - if (nlines > 1) doc.remove(from.line + 1, nlines - 1) - doc.insert(from.line + 1, added) - } - - signalLater(doc, "change", doc, change) -} - -// Call f for all linked documents. -export function linkedDocs(doc, f, sharedHistOnly) { - function propagate(doc, skip, sharedHist) { - if (doc.linked) for (let i = 0; i < doc.linked.length; ++i) { - let rel = doc.linked[i] - if (rel.doc == skip) continue - let shared = sharedHist && rel.sharedHist - if (sharedHistOnly && !shared) continue - f(rel.doc, shared) - propagate(rel.doc, doc, shared) - } - } - propagate(doc, null, true) -} - -// Attach a document to an editor. -export function attachDoc(cm, doc) { - if (doc.cm) throw new Error("This document is already in use.") - cm.doc = doc - doc.cm = cm - estimateLineHeights(cm) - loadMode(cm) - setDirectionClass(cm) - if (!cm.options.lineWrapping) findMaxLine(cm) - cm.options.mode = doc.modeOption - regChange(cm) -} - -function setDirectionClass(cm) { - ;(cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl") -} - -export function directionChanged(cm) { - runInOp(cm, () => { - setDirectionClass(cm) - regChange(cm) - }) -} diff --git a/CODE/js/codemirror/src/model/history.js b/CODE/js/codemirror/src/model/history.js deleted file mode 100644 index 2d9359f0..00000000 --- a/CODE/js/codemirror/src/model/history.js +++ /dev/null @@ -1,228 +0,0 @@ -import { cmp, copyPos } from "../line/pos.js" -import { stretchSpansOverChange } from "../line/spans.js" -import { getBetween } from "../line/utils_line.js" -import { signal } from "../util/event.js" -import { indexOf, lst } from "../util/misc.js" - -import { changeEnd } from "./change_measurement.js" -import { linkedDocs } from "./document_data.js" -import { Selection } from "./selection.js" - -export function History(startGen) { - // Arrays of change events and selections. Doing something adds an - // event to done and clears undo. Undoing moves events from done - // to undone, redoing moves them in the other direction. - this.done = []; this.undone = [] - this.undoDepth = Infinity - // Used to track when changes can be merged into a single undo - // event - this.lastModTime = this.lastSelTime = 0 - this.lastOp = this.lastSelOp = null - this.lastOrigin = this.lastSelOrigin = null - // Used by the isClean() method - this.generation = this.maxGeneration = startGen || 1 -} - -// Create a history change event from an updateDoc-style change -// object. -export function historyChangeFromChange(doc, change) { - let histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)} - attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1) - linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true) - return histChange -} - -// Pop all selection events off the end of a history array. Stop at -// a change event. -function clearSelectionEvents(array) { - while (array.length) { - let last = lst(array) - if (last.ranges) array.pop() - else break - } -} - -// Find the top change event in the history. Pop off selection -// events that are in the way. -function lastChangeEvent(hist, force) { - if (force) { - clearSelectionEvents(hist.done) - return lst(hist.done) - } else if (hist.done.length && !lst(hist.done).ranges) { - return lst(hist.done) - } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { - hist.done.pop() - return lst(hist.done) - } -} - -// Register a change in the history. Merges changes that are within -// a single operation, or are close together with an origin that -// allows merging (starting with "+") into a single event. -export function addChangeToHistory(doc, change, selAfter, opId) { - let hist = doc.history - hist.undone.length = 0 - let time = +new Date, cur - let last - - if ((hist.lastOp == opId || - hist.lastOrigin == change.origin && change.origin && - ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || - change.origin.charAt(0) == "*")) && - (cur = lastChangeEvent(hist, hist.lastOp == opId))) { - // Merge this change into the last event - last = lst(cur.changes) - if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { - // Optimized case for simple insertion -- don't want to add - // new changesets for every character typed - last.to = changeEnd(change) - } else { - // Add new sub-event - cur.changes.push(historyChangeFromChange(doc, change)) - } - } else { - // Can not be merged, start a new event. - let before = lst(hist.done) - if (!before || !before.ranges) - pushSelectionToHistory(doc.sel, hist.done) - cur = {changes: [historyChangeFromChange(doc, change)], - generation: hist.generation} - hist.done.push(cur) - while (hist.done.length > hist.undoDepth) { - hist.done.shift() - if (!hist.done[0].ranges) hist.done.shift() - } - } - hist.done.push(selAfter) - hist.generation = ++hist.maxGeneration - hist.lastModTime = hist.lastSelTime = time - hist.lastOp = hist.lastSelOp = opId - hist.lastOrigin = hist.lastSelOrigin = change.origin - - if (!last) signal(doc, "historyAdded") -} - -function selectionEventCanBeMerged(doc, origin, prev, sel) { - let ch = origin.charAt(0) - return ch == "*" || - ch == "+" && - prev.ranges.length == sel.ranges.length && - prev.somethingSelected() == sel.somethingSelected() && - new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) -} - -// Called whenever the selection changes, sets the new selection as -// the pending selection in the history, and pushes the old pending -// selection into the 'done' array when it was significantly -// different (in number of selected ranges, emptiness, or time). -export function addSelectionToHistory(doc, sel, opId, options) { - let hist = doc.history, origin = options && options.origin - - // A new event is started when the previous origin does not match - // the current, or the origins don't allow matching. Origins - // starting with * are always merged, those starting with + are - // merged when similar and close together in time. - if (opId == hist.lastSelOp || - (origin && hist.lastSelOrigin == origin && - (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || - selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) - hist.done[hist.done.length - 1] = sel - else - pushSelectionToHistory(sel, hist.done) - - hist.lastSelTime = +new Date - hist.lastSelOrigin = origin - hist.lastSelOp = opId - if (options && options.clearRedo !== false) - clearSelectionEvents(hist.undone) -} - -export function pushSelectionToHistory(sel, dest) { - let top = lst(dest) - if (!(top && top.ranges && top.equals(sel))) - dest.push(sel) -} - -// Used to store marked span information in the history. -function attachLocalSpans(doc, change, from, to) { - let existing = change["spans_" + doc.id], n = 0 - doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => { - if (line.markedSpans) - (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans - ++n - }) -} - -// When un/re-doing restores text containing marked spans, those -// that have been explicitly cleared should not be restored. -function removeClearedSpans(spans) { - if (!spans) return null - let out - for (let i = 0; i < spans.length; ++i) { - if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i) } - else if (out) out.push(spans[i]) - } - return !out ? spans : out.length ? out : null -} - -// Retrieve and filter the old marked spans stored in a change event. -function getOldSpans(doc, change) { - let found = change["spans_" + doc.id] - if (!found) return null - let nw = [] - for (let i = 0; i < change.text.length; ++i) - nw.push(removeClearedSpans(found[i])) - return nw -} - -// Used for un/re-doing changes from the history. Combines the -// result of computing the existing spans with the set of spans that -// existed in the history (so that deleting around a span and then -// undoing brings back the span). -export function mergeOldSpans(doc, change) { - let old = getOldSpans(doc, change) - let stretched = stretchSpansOverChange(doc, change) - if (!old) return stretched - if (!stretched) return old - - for (let i = 0; i < old.length; ++i) { - let oldCur = old[i], stretchCur = stretched[i] - if (oldCur && stretchCur) { - spans: for (let j = 0; j < stretchCur.length; ++j) { - let span = stretchCur[j] - for (let k = 0; k < oldCur.length; ++k) - if (oldCur[k].marker == span.marker) continue spans - oldCur.push(span) - } - } else if (stretchCur) { - old[i] = stretchCur - } - } - return old -} - -// Used both to provide a JSON-safe object in .getHistory, and, when -// detaching a document, to split the history in two -export function copyHistoryArray(events, newGroup, instantiateSel) { - let copy = [] - for (let i = 0; i < events.length; ++i) { - let event = events[i] - if (event.ranges) { - copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) - continue - } - let changes = event.changes, newChanges = [] - copy.push({changes: newChanges}) - for (let j = 0; j < changes.length; ++j) { - let change = changes[j], m - newChanges.push({from: change.from, to: change.to, text: change.text}) - if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { - if (indexOf(newGroup, Number(m[1])) > -1) { - lst(newChanges)[prop] = change[prop] - delete change[prop] - } - } - } - } - return copy -} diff --git a/CODE/js/codemirror/src/model/line_widget.js b/CODE/js/codemirror/src/model/line_widget.js deleted file mode 100644 index f94727e5..00000000 --- a/CODE/js/codemirror/src/model/line_widget.js +++ /dev/null @@ -1,78 +0,0 @@ -import { runInOp } from "../display/operations.js" -import { addToScrollTop } from "../display/scrolling.js" -import { regLineChange } from "../display/view_tracking.js" -import { heightAtLine, lineIsHidden } from "../line/spans.js" -import { lineNo, updateLineHeight } from "../line/utils_line.js" -import { widgetHeight } from "../measurement/widgets.js" -import { changeLine } from "./changes.js" -import { eventMixin } from "../util/event.js" -import { signalLater } from "../util/operation_group.js" - -// Line widgets are block elements displayed above or below a line. - -export class LineWidget { - constructor(doc, node, options) { - if (options) for (let opt in options) if (options.hasOwnProperty(opt)) - this[opt] = options[opt] - this.doc = doc - this.node = node - } - - clear() { - let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) - if (no == null || !ws) return - for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) - if (!ws.length) line.widgets = null - let height = widgetHeight(this) - updateLineHeight(line, Math.max(0, line.height - height)) - if (cm) { - runInOp(cm, () => { - adjustScrollWhenAboveVisible(cm, line, -height) - regLineChange(cm, no, "widget") - }) - signalLater(cm, "lineWidgetCleared", cm, this, no) - } - } - - changed() { - let oldH = this.height, cm = this.doc.cm, line = this.line - this.height = null - let diff = widgetHeight(this) - oldH - if (!diff) return - if (!lineIsHidden(this.doc, line)) updateLineHeight(line, line.height + diff) - if (cm) { - runInOp(cm, () => { - cm.curOp.forceUpdate = true - adjustScrollWhenAboveVisible(cm, line, diff) - signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line)) - }) - } - } -} -eventMixin(LineWidget) - -function adjustScrollWhenAboveVisible(cm, line, diff) { - if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) - addToScrollTop(cm, diff) -} - -export function addLineWidget(doc, handle, node, options) { - let widget = new LineWidget(doc, node, options) - let cm = doc.cm - if (cm && widget.noHScroll) cm.display.alignWidgets = true - changeLine(doc, handle, "widget", line => { - let widgets = line.widgets || (line.widgets = []) - if (widget.insertAt == null) widgets.push(widget) - else widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget) - widget.line = line - if (cm && !lineIsHidden(doc, line)) { - let aboveVisible = heightAtLine(line) < doc.scrollTop - updateLineHeight(line, line.height + widgetHeight(widget)) - if (aboveVisible) addToScrollTop(cm, widget.height) - cm.curOp.forceUpdate = true - } - return true - }) - if (cm) signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) - return widget -} diff --git a/CODE/js/codemirror/src/model/mark_text.js b/CODE/js/codemirror/src/model/mark_text.js deleted file mode 100644 index 088f9c98..00000000 --- a/CODE/js/codemirror/src/model/mark_text.js +++ /dev/null @@ -1,293 +0,0 @@ -import { eltP } from "../util/dom.js" -import { eventMixin, hasHandler, on } from "../util/event.js" -import { endOperation, operation, runInOp, startOperation } from "../display/operations.js" -import { clipPos, cmp, Pos } from "../line/pos.js" -import { lineNo, updateLineHeight } from "../line/utils_line.js" -import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js" -import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js" -import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js" -import { copyObj, indexOf, lst } from "../util/misc.js" -import { signalLater } from "../util/operation_group.js" -import { widgetHeight } from "../measurement/widgets.js" -import { regChange, regLineChange } from "../display/view_tracking.js" - -import { linkedDocs } from "./document_data.js" -import { addChangeToHistory } from "./history.js" -import { reCheckSelection } from "./selection_updates.js" - -// TEXTMARKERS - -// Created with markText and setBookmark methods. A TextMarker is a -// handle that can be used to clear or find a marked position in the -// document. Line objects hold arrays (markedSpans) containing -// {from, to, marker} object pointing to such marker objects, and -// indicating that such a marker is present on that line. Multiple -// lines may point to the same marker when it spans across lines. -// The spans will have null for their from/to properties when the -// marker continues beyond the start/end of the line. Markers have -// links back to the lines they currently touch. - -// Collapsed markers have unique ids, in order to be able to order -// them, which is needed for uniquely determining an outer marker -// when they overlap (they may nest, but not partially overlap). -let nextMarkerId = 0 - -export class TextMarker { - constructor(doc, type) { - this.lines = [] - this.type = type - this.doc = doc - this.id = ++nextMarkerId - } - - // Clear the marker. - clear() { - if (this.explicitlyCleared) return - let cm = this.doc.cm, withOp = cm && !cm.curOp - if (withOp) startOperation(cm) - if (hasHandler(this, "clear")) { - let found = this.find() - if (found) signalLater(this, "clear", found.from, found.to) - } - let min = null, max = null - for (let i = 0; i < this.lines.length; ++i) { - let line = this.lines[i] - let span = getMarkedSpanFor(line.markedSpans, this) - if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") - else if (cm) { - if (span.to != null) max = lineNo(line) - if (span.from != null) min = lineNo(line) - } - line.markedSpans = removeMarkedSpan(line.markedSpans, span) - if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) - updateLineHeight(line, textHeight(cm.display)) - } - if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { - let visual = visualLine(this.lines[i]), len = lineLength(visual) - if (len > cm.display.maxLineLength) { - cm.display.maxLine = visual - cm.display.maxLineLength = len - cm.display.maxLineChanged = true - } - } - - if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) - this.lines.length = 0 - this.explicitlyCleared = true - if (this.atomic && this.doc.cantEdit) { - this.doc.cantEdit = false - if (cm) reCheckSelection(cm.doc) - } - if (cm) signalLater(cm, "markerCleared", cm, this, min, max) - if (withOp) endOperation(cm) - if (this.parent) this.parent.clear() - } - - // Find the position of the marker in the document. Returns a {from, - // to} object by default. Side can be passed to get a specific side - // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the - // Pos objects returned contain a line object, rather than a line - // number (used to prevent looking up the same line twice). - find(side, lineObj) { - if (side == null && this.type == "bookmark") side = 1 - let from, to - for (let i = 0; i < this.lines.length; ++i) { - let line = this.lines[i] - let span = getMarkedSpanFor(line.markedSpans, this) - if (span.from != null) { - from = Pos(lineObj ? line : lineNo(line), span.from) - if (side == -1) return from - } - if (span.to != null) { - to = Pos(lineObj ? line : lineNo(line), span.to) - if (side == 1) return to - } - } - return from && {from: from, to: to} - } - - // Signals that the marker's widget changed, and surrounding layout - // should be recomputed. - changed() { - let pos = this.find(-1, true), widget = this, cm = this.doc.cm - if (!pos || !cm) return - runInOp(cm, () => { - let line = pos.line, lineN = lineNo(pos.line) - let view = findViewForLine(cm, lineN) - if (view) { - clearLineMeasurementCacheFor(view) - cm.curOp.selectionChanged = cm.curOp.forceUpdate = true - } - cm.curOp.updateMaxLine = true - if (!lineIsHidden(widget.doc, line) && widget.height != null) { - let oldHeight = widget.height - widget.height = null - let dHeight = widgetHeight(widget) - oldHeight - if (dHeight) - updateLineHeight(line, line.height + dHeight) - } - signalLater(cm, "markerChanged", cm, this) - }) - } - - attachLine(line) { - if (!this.lines.length && this.doc.cm) { - let op = this.doc.cm.curOp - if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) - (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) - } - this.lines.push(line) - } - - detachLine(line) { - this.lines.splice(indexOf(this.lines, line), 1) - if (!this.lines.length && this.doc.cm) { - let op = this.doc.cm.curOp - ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) - } - } -} -eventMixin(TextMarker) - -// Create a marker, wire it up to the right lines, and -export function markText(doc, from, to, options, type) { - // Shared markers (across linked documents) are handled separately - // (markTextShared will call out to this again, once per - // document). - if (options && options.shared) return markTextShared(doc, from, to, options, type) - // Ensure we are in an operation. - if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type) - - let marker = new TextMarker(doc, type), diff = cmp(from, to) - if (options) copyObj(options, marker, false) - // Don't connect empty markers unless clearWhenEmpty is false - if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) - return marker - if (marker.replacedWith) { - // Showing up as a widget implies collapsed (widget replaces text) - marker.collapsed = true - marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget") - if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true") - if (options.insertLeft) marker.widgetNode.insertLeft = true - } - if (marker.collapsed) { - if (conflictingCollapsedRange(doc, from.line, from, to, marker) || - from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) - throw new Error("Inserting collapsed marker partially overlapping an existing one") - seeCollapsedSpans() - } - - if (marker.addToHistory) - addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) - - let curLine = from.line, cm = doc.cm, updateMaxLine - doc.iter(curLine, to.line + 1, line => { - if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) - updateMaxLine = true - if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0) - addMarkedSpan(line, new MarkedSpan(marker, - curLine == from.line ? from.ch : null, - curLine == to.line ? to.ch : null)) - ++curLine - }) - // lineIsHidden depends on the presence of the spans, so needs a second pass - if (marker.collapsed) doc.iter(from.line, to.line + 1, line => { - if (lineIsHidden(doc, line)) updateLineHeight(line, 0) - }) - - if (marker.clearOnEnter) on(marker, "beforeCursorEnter", () => marker.clear()) - - if (marker.readOnly) { - seeReadOnlySpans() - if (doc.history.done.length || doc.history.undone.length) - doc.clearHistory() - } - if (marker.collapsed) { - marker.id = ++nextMarkerId - marker.atomic = true - } - if (cm) { - // Sync editor state - if (updateMaxLine) cm.curOp.updateMaxLine = true - if (marker.collapsed) - regChange(cm, from.line, to.line + 1) - else if (marker.className || marker.startStyle || marker.endStyle || marker.css || - marker.attributes || marker.title) - for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") - if (marker.atomic) reCheckSelection(cm.doc) - signalLater(cm, "markerAdded", cm, marker) - } - return marker -} - -// SHARED TEXTMARKERS - -// A shared marker spans multiple linked documents. It is -// implemented as a meta-marker-object controlling multiple normal -// markers. -export class SharedTextMarker { - constructor(markers, primary) { - this.markers = markers - this.primary = primary - for (let i = 0; i < markers.length; ++i) - markers[i].parent = this - } - - clear() { - if (this.explicitlyCleared) return - this.explicitlyCleared = true - for (let i = 0; i < this.markers.length; ++i) - this.markers[i].clear() - signalLater(this, "clear") - } - - find(side, lineObj) { - return this.primary.find(side, lineObj) - } -} -eventMixin(SharedTextMarker) - -function markTextShared(doc, from, to, options, type) { - options = copyObj(options) - options.shared = false - let markers = [markText(doc, from, to, options, type)], primary = markers[0] - let widget = options.widgetNode - linkedDocs(doc, doc => { - if (widget) options.widgetNode = widget.cloneNode(true) - markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) - for (let i = 0; i < doc.linked.length; ++i) - if (doc.linked[i].isParent) return - primary = lst(markers) - }) - return new SharedTextMarker(markers, primary) -} - -export function findSharedMarkers(doc) { - return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent) -} - -export function copySharedMarkers(doc, markers) { - for (let i = 0; i < markers.length; i++) { - let marker = markers[i], pos = marker.find() - let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) - if (cmp(mFrom, mTo)) { - let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) - marker.markers.push(subMark) - subMark.parent = marker - } - } -} - -export function detachSharedMarkers(markers) { - for (let i = 0; i < markers.length; i++) { - let marker = markers[i], linked = [marker.primary.doc] - linkedDocs(marker.primary.doc, d => linked.push(d)) - for (let j = 0; j < marker.markers.length; j++) { - let subMarker = marker.markers[j] - if (indexOf(linked, subMarker.doc) == -1) { - subMarker.parent = null - marker.markers.splice(j--, 1) - } - } - } -} diff --git a/CODE/js/codemirror/src/model/selection.js b/CODE/js/codemirror/src/model/selection.js deleted file mode 100644 index 793cb4ca..00000000 --- a/CODE/js/codemirror/src/model/selection.js +++ /dev/null @@ -1,84 +0,0 @@ -import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js" -import { indexOf } from "../util/misc.js" - -// Selection objects are immutable. A new one is created every time -// the selection changes. A selection is one or more non-overlapping -// (and non-touching) ranges, sorted, and an integer that indicates -// which one is the primary selection (the one that's scrolled into -// view, that getCursor returns, etc). -export class Selection { - constructor(ranges, primIndex) { - this.ranges = ranges - this.primIndex = primIndex - } - - primary() { return this.ranges[this.primIndex] } - - equals(other) { - if (other == this) return true - if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false - for (let i = 0; i < this.ranges.length; i++) { - let here = this.ranges[i], there = other.ranges[i] - if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false - } - return true - } - - deepCopy() { - let out = [] - for (let i = 0; i < this.ranges.length; i++) - out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)) - return new Selection(out, this.primIndex) - } - - somethingSelected() { - for (let i = 0; i < this.ranges.length; i++) - if (!this.ranges[i].empty()) return true - return false - } - - contains(pos, end) { - if (!end) end = pos - for (let i = 0; i < this.ranges.length; i++) { - let range = this.ranges[i] - if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) - return i - } - return -1 - } -} - -export class Range { - constructor(anchor, head) { - this.anchor = anchor; this.head = head - } - - from() { return minPos(this.anchor, this.head) } - to() { return maxPos(this.anchor, this.head) } - empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch } -} - -// Take an unsorted, potentially overlapping set of ranges, and -// build a selection out of it. 'Consumes' ranges array (modifying -// it). -export function normalizeSelection(cm, ranges, primIndex) { - let mayTouch = cm && cm.options.selectionsMayTouch - let prim = ranges[primIndex] - ranges.sort((a, b) => cmp(a.from(), b.from())) - primIndex = indexOf(ranges, prim) - for (let i = 1; i < ranges.length; i++) { - let cur = ranges[i], prev = ranges[i - 1] - let diff = cmp(prev.to(), cur.from()) - if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { - let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) - let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head - if (i <= primIndex) --primIndex - ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)) - } - } - return new Selection(ranges, primIndex) -} - -export function simpleSelection(anchor, head) { - return new Selection([new Range(anchor, head || anchor)], 0) -} diff --git a/CODE/js/codemirror/src/model/selection_updates.js b/CODE/js/codemirror/src/model/selection_updates.js deleted file mode 100644 index 4db2bd7f..00000000 --- a/CODE/js/codemirror/src/model/selection_updates.js +++ /dev/null @@ -1,216 +0,0 @@ -import { signalLater } from "../util/operation_group.js" -import { ensureCursorVisible } from "../display/scrolling.js" -import { clipPos, cmp, Pos } from "../line/pos.js" -import { getLine } from "../line/utils_line.js" -import { hasHandler, signal, signalCursorActivity } from "../util/event.js" -import { lst, sel_dontScroll } from "../util/misc.js" - -import { addSelectionToHistory } from "./history.js" -import { normalizeSelection, Range, Selection, simpleSelection } from "./selection.js" - -// The 'scroll' parameter given to many of these indicated whether -// the new cursor position should be scrolled into view after -// modifying the selection. - -// If shift is held or the extend flag is set, extends a range to -// include a given position (and optionally a second position). -// Otherwise, simply returns the range between the given positions. -// Used for cursor motion and such. -export function extendRange(range, head, other, extend) { - if (extend) { - let anchor = range.anchor - if (other) { - let posBefore = cmp(head, anchor) < 0 - if (posBefore != (cmp(other, anchor) < 0)) { - anchor = head - head = other - } else if (posBefore != (cmp(head, other) < 0)) { - head = other - } - } - return new Range(anchor, head) - } else { - return new Range(other || head, head) - } -} - -// Extend the primary selection range, discard the rest. -export function extendSelection(doc, head, other, options, extend) { - if (extend == null) extend = doc.cm && (doc.cm.display.shift || doc.extend) - setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options) -} - -// Extend all selections (pos is an array of selections with length -// equal the number of selections) -export function extendSelections(doc, heads, options) { - let out = [] - let extend = doc.cm && (doc.cm.display.shift || doc.extend) - for (let i = 0; i < doc.sel.ranges.length; i++) - out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend) - let newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex) - setSelection(doc, newSel, options) -} - -// Updates a single range in the selection. -export function replaceOneSelection(doc, i, range, options) { - let ranges = doc.sel.ranges.slice(0) - ranges[i] = range - setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options) -} - -// Reset the selection to a single range. -export function setSimpleSelection(doc, anchor, head, options) { - setSelection(doc, simpleSelection(anchor, head), options) -} - -// Give beforeSelectionChange handlers a change to influence a -// selection update. -function filterSelectionChange(doc, sel, options) { - let obj = { - ranges: sel.ranges, - update: function(ranges) { - this.ranges = [] - for (let i = 0; i < ranges.length; i++) - this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), - clipPos(doc, ranges[i].head)) - }, - origin: options && options.origin - } - signal(doc, "beforeSelectionChange", doc, obj) - if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj) - if (obj.ranges != sel.ranges) return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) - else return sel -} - -export function setSelectionReplaceHistory(doc, sel, options) { - let done = doc.history.done, last = lst(done) - if (last && last.ranges) { - done[done.length - 1] = sel - setSelectionNoUndo(doc, sel, options) - } else { - setSelection(doc, sel, options) - } -} - -// Set a new selection. -export function setSelection(doc, sel, options) { - setSelectionNoUndo(doc, sel, options) - addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) -} - -export function setSelectionNoUndo(doc, sel, options) { - if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) - sel = filterSelectionChange(doc, sel, options) - - let bias = options && options.bias || - (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1) - setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)) - - if (!(options && options.scroll === false) && doc.cm) - ensureCursorVisible(doc.cm) -} - -function setSelectionInner(doc, sel) { - if (sel.equals(doc.sel)) return - - doc.sel = sel - - if (doc.cm) { - doc.cm.curOp.updateInput = 1 - doc.cm.curOp.selectionChanged = true - signalCursorActivity(doc.cm) - } - signalLater(doc, "cursorActivity", doc) -} - -// Verify that the selection does not partially select any atomic -// marked ranges. -export function reCheckSelection(doc) { - setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)) -} - -// Return a selection that does not partially select any atomic -// ranges. -function skipAtomicInSelection(doc, sel, bias, mayClear) { - let out - for (let i = 0; i < sel.ranges.length; i++) { - let range = sel.ranges[i] - let old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] - let newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) - let newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) - if (out || newAnchor != range.anchor || newHead != range.head) { - if (!out) out = sel.ranges.slice(0, i) - out[i] = new Range(newAnchor, newHead) - } - } - return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel -} - -function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { - let line = getLine(doc, pos.line) - if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { - let sp = line.markedSpans[i], m = sp.marker - - // Determine if we should prevent the cursor being placed to the left/right of an atomic marker - // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it - // is with selectLeft/Right - let preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft - let preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight - - if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && - (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { - if (mayClear) { - signal(m, "beforeCursorEnter") - if (m.explicitlyCleared) { - if (!line.markedSpans) break - else {--i; continue} - } - } - if (!m.atomic) continue - - if (oldPos) { - let near = m.find(dir < 0 ? 1 : -1), diff - if (dir < 0 ? preventCursorRight : preventCursorLeft) - near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) - if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) - return skipAtomicInner(doc, near, pos, dir, mayClear) - } - - let far = m.find(dir < 0 ? -1 : 1) - if (dir < 0 ? preventCursorLeft : preventCursorRight) - far = movePos(doc, far, dir, far.line == pos.line ? line : null) - return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null - } - } - return pos -} - -// Ensure a given position is not inside an atomic range. -export function skipAtomic(doc, pos, oldPos, bias, mayClear) { - let dir = bias || 1 - let found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || - (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || - skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || - (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)) - if (!found) { - doc.cantEdit = true - return Pos(doc.first, 0) - } - return found -} - -function movePos(doc, pos, dir, line) { - if (dir < 0 && pos.ch == 0) { - if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)) - else return null - } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { - if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0) - else return null - } else { - return new Pos(pos.line, pos.ch + dir) - } -} - -export function selectAll(cm) { - cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll) -} diff --git a/CODE/js/codemirror/src/modes.js b/CODE/js/codemirror/src/modes.js deleted file mode 100644 index 83845170..00000000 --- a/CODE/js/codemirror/src/modes.js +++ /dev/null @@ -1,96 +0,0 @@ -import { copyObj, createObj } from "./util/misc.js" - -// Known modes, by name and by MIME -export let modes = {}, mimeModes = {} - -// Extra arguments are stored as the mode's dependencies, which is -// used by (legacy) mechanisms like loadmode.js to automatically -// load a mode. (Preferred mechanism is the require/define calls.) -export function defineMode(name, mode) { - if (arguments.length > 2) - mode.dependencies = Array.prototype.slice.call(arguments, 2) - modes[name] = mode -} - -export function defineMIME(mime, spec) { - mimeModes[mime] = spec -} - -// Given a MIME type, a {name, ...options} config object, or a name -// string, return a mode config object. -export function resolveMode(spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { - spec = mimeModes[spec] - } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { - let found = mimeModes[spec.name] - if (typeof found == "string") found = {name: found} - spec = createObj(found, spec) - spec.name = found.name - } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { - return resolveMode("application/xml") - } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { - return resolveMode("application/json") - } - if (typeof spec == "string") return {name: spec} - else return spec || {name: "null"} -} - -// Given a mode spec (anything that resolveMode accepts), find and -// initialize an actual mode object. -export function getMode(options, spec) { - spec = resolveMode(spec) - let mfactory = modes[spec.name] - if (!mfactory) return getMode(options, "text/plain") - let modeObj = mfactory(options, spec) - if (modeExtensions.hasOwnProperty(spec.name)) { - let exts = modeExtensions[spec.name] - for (let prop in exts) { - if (!exts.hasOwnProperty(prop)) continue - if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop] - modeObj[prop] = exts[prop] - } - } - modeObj.name = spec.name - if (spec.helperType) modeObj.helperType = spec.helperType - if (spec.modeProps) for (let prop in spec.modeProps) - modeObj[prop] = spec.modeProps[prop] - - return modeObj -} - -// This can be used to attach properties to mode objects from -// outside the actual mode definition. -export let modeExtensions = {} -export function extendMode(mode, properties) { - let exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) - copyObj(properties, exts) -} - -export function copyState(mode, state) { - if (state === true) return state - if (mode.copyState) return mode.copyState(state) - let nstate = {} - for (let n in state) { - let val = state[n] - if (val instanceof Array) val = val.concat([]) - nstate[n] = val - } - return nstate -} - -// Given a mode and a state (for that mode), find the inner mode and -// state at the position that the state refers to. -export function innerMode(mode, state) { - let info - while (mode.innerMode) { - info = mode.innerMode(state) - if (!info || info.mode == mode) break - state = info.state - mode = info.mode - } - return info || {mode: mode, state: state} -} - -export function startState(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true -} diff --git a/CODE/js/codemirror/src/util/StringStream.js b/CODE/js/codemirror/src/util/StringStream.js deleted file mode 100644 index 022c4bc2..00000000 --- a/CODE/js/codemirror/src/util/StringStream.js +++ /dev/null @@ -1,90 +0,0 @@ -import { countColumn } from "./misc.js" - -// STRING STREAM - -// Fed to the mode parsers, provides helper functions to make -// parsers more succinct. - -class StringStream { - constructor(string, tabSize, lineOracle) { - this.pos = this.start = 0 - this.string = string - this.tabSize = tabSize || 8 - this.lastColumnPos = this.lastColumnValue = 0 - this.lineStart = 0 - this.lineOracle = lineOracle - } - - eol() {return this.pos >= this.string.length} - sol() {return this.pos == this.lineStart} - peek() {return this.string.charAt(this.pos) || undefined} - next() { - if (this.pos < this.string.length) - return this.string.charAt(this.pos++) - } - eat(match) { - let ch = this.string.charAt(this.pos) - let ok - if (typeof match == "string") ok = ch == match - else ok = ch && (match.test ? match.test(ch) : match(ch)) - if (ok) {++this.pos; return ch} - } - eatWhile(match) { - let start = this.pos - while (this.eat(match)){} - return this.pos > start - } - eatSpace() { - let start = this.pos - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos - return this.pos > start - } - skipToEnd() {this.pos = this.string.length} - skipTo(ch) { - let found = this.string.indexOf(ch, this.pos) - if (found > -1) {this.pos = found; return true} - } - backUp(n) {this.pos -= n} - column() { - if (this.lastColumnPos < this.start) { - this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue) - this.lastColumnPos = this.start - } - return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) - } - indentation() { - return countColumn(this.string, null, this.tabSize) - - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) - } - match(pattern, consume, caseInsensitive) { - if (typeof pattern == "string") { - let cased = str => caseInsensitive ? str.toLowerCase() : str - let substr = this.string.substr(this.pos, pattern.length) - if (cased(substr) == cased(pattern)) { - if (consume !== false) this.pos += pattern.length - return true - } - } else { - let match = this.string.slice(this.pos).match(pattern) - if (match && match.index > 0) return null - if (match && consume !== false) this.pos += match[0].length - return match - } - } - current(){return this.string.slice(this.start, this.pos)} - hideFirstChars(n, inner) { - this.lineStart += n - try { return inner() } - finally { this.lineStart -= n } - } - lookAhead(n) { - let oracle = this.lineOracle - return oracle && oracle.lookAhead(n) - } - baseToken() { - let oracle = this.lineOracle - return oracle && oracle.baseToken(this.pos) - } -} - -export default StringStream diff --git a/CODE/js/codemirror/src/util/bidi.js b/CODE/js/codemirror/src/util/bidi.js deleted file mode 100644 index 92c4191d..00000000 --- a/CODE/js/codemirror/src/util/bidi.js +++ /dev/null @@ -1,215 +0,0 @@ -import { lst } from "./misc.js" - -// BIDI HELPERS - -export function iterateBidiSections(order, from, to, f) { - if (!order) return f(from, to, "ltr", 0) - let found = false - for (let i = 0; i < order.length; ++i) { - let part = order[i] - if (part.from < to && part.to > from || from == to && part.to == from) { - f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i) - found = true - } - } - if (!found) f(from, to, "ltr") -} - -export let bidiOther = null -export function getBidiPartAt(order, ch, sticky) { - let found - bidiOther = null - for (let i = 0; i < order.length; ++i) { - let cur = order[i] - if (cur.from < ch && cur.to > ch) return i - if (cur.to == ch) { - if (cur.from != cur.to && sticky == "before") found = i - else bidiOther = i - } - if (cur.from == ch) { - if (cur.from != cur.to && sticky != "before") found = i - else bidiOther = i - } - } - return found != null ? found : bidiOther -} - -// Bidirectional ordering algorithm -// See http://unicode.org/reports/tr9/tr9-13.html for the algorithm -// that this (partially) implements. - -// One-char codes used for character types: -// L (L): Left-to-Right -// R (R): Right-to-Left -// r (AL): Right-to-Left Arabic -// 1 (EN): European Number -// + (ES): European Number Separator -// % (ET): European Number Terminator -// n (AN): Arabic Number -// , (CS): Common Number Separator -// m (NSM): Non-Spacing Mark -// b (BN): Boundary Neutral -// s (B): Paragraph Separator -// t (S): Segment Separator -// w (WS): Whitespace -// N (ON): Other Neutrals - -// Returns null if characters are ordered as they appear -// (left-to-right), or an array of sections ({from, to, level} -// objects) in the order in which they occur visually. -let bidiOrdering = (function() { - // Character types for codepoints 0 to 0xff - let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" - // Character types for codepoints 0x600 to 0x6f9 - let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111" - function charType(code) { - if (code <= 0xf7) return lowTypes.charAt(code) - else if (0x590 <= code && code <= 0x5f4) return "R" - else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600) - else if (0x6ee <= code && code <= 0x8ac) return "r" - else if (0x2000 <= code && code <= 0x200b) return "w" - else if (code == 0x200c) return "b" - else return "L" - } - - let bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ - let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ - - function BidiSpan(level, from, to) { - this.level = level - this.from = from; this.to = to - } - - return function(str, direction) { - let outerType = direction == "ltr" ? "L" : "R" - - if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) return false - let len = str.length, types = [] - for (let i = 0; i < len; ++i) - types.push(charType(str.charCodeAt(i))) - - // W1. Examine each non-spacing mark (NSM) in the level run, and - // change the type of the NSM to the type of the previous - // character. If the NSM is at the start of the level run, it will - // get the type of sor. - for (let i = 0, prev = outerType; i < len; ++i) { - let type = types[i] - if (type == "m") types[i] = prev - else prev = type - } - - // W2. Search backwards from each instance of a European number - // until the first strong type (R, L, AL, or sor) is found. If an - // AL is found, change the type of the European number to Arabic - // number. - // W3. Change all ALs to R. - for (let i = 0, cur = outerType; i < len; ++i) { - let type = types[i] - if (type == "1" && cur == "r") types[i] = "n" - else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" } - } - - // W4. A single European separator between two European numbers - // changes to a European number. A single common separator between - // two numbers of the same type changes to that type. - for (let i = 1, prev = types[0]; i < len - 1; ++i) { - let type = types[i] - if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1" - else if (type == "," && prev == types[i+1] && - (prev == "1" || prev == "n")) types[i] = prev - prev = type - } - - // W5. A sequence of European terminators adjacent to European - // numbers changes to all European numbers. - // W6. Otherwise, separators and terminators change to Other - // Neutral. - for (let i = 0; i < len; ++i) { - let type = types[i] - if (type == ",") types[i] = "N" - else if (type == "%") { - let end - for (end = i + 1; end < len && types[end] == "%"; ++end) {} - let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N" - for (let j = i; j < end; ++j) types[j] = replace - i = end - 1 - } - } - - // W7. Search backwards from each instance of a European number - // until the first strong type (R, L, or sor) is found. If an L is - // found, then change the type of the European number to L. - for (let i = 0, cur = outerType; i < len; ++i) { - let type = types[i] - if (cur == "L" && type == "1") types[i] = "L" - else if (isStrong.test(type)) cur = type - } - - // N1. A sequence of neutrals takes the direction of the - // surrounding strong text if the text on both sides has the same - // direction. European and Arabic numbers act as if they were R in - // terms of their influence on neutrals. Start-of-level-run (sor) - // and end-of-level-run (eor) are used at level run boundaries. - // N2. Any remaining neutrals take the embedding direction. - for (let i = 0; i < len; ++i) { - if (isNeutral.test(types[i])) { - let end - for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} - let before = (i ? types[i-1] : outerType) == "L" - let after = (end < len ? types[end] : outerType) == "L" - let replace = before == after ? (before ? "L" : "R") : outerType - for (let j = i; j < end; ++j) types[j] = replace - i = end - 1 - } - } - - // Here we depart from the documented algorithm, in order to avoid - // building up an actual levels array. Since there are only three - // levels (0, 1, 2) in an implementation that doesn't take - // explicit embedding into account, we can build up the order on - // the fly, without following the level-based algorithm. - let order = [], m - for (let i = 0; i < len;) { - if (countsAsLeft.test(types[i])) { - let start = i - for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} - order.push(new BidiSpan(0, start, i)) - } else { - let pos = i, at = order.length, isRTL = direction == "rtl" ? 1 : 0 - for (++i; i < len && types[i] != "L"; ++i) {} - for (let j = pos; j < i;) { - if (countsAsNum.test(types[j])) { - if (pos < j) { order.splice(at, 0, new BidiSpan(1, pos, j)); at += isRTL } - let nstart = j - for (++j; j < i && countsAsNum.test(types[j]); ++j) {} - order.splice(at, 0, new BidiSpan(2, nstart, j)) - at += isRTL - pos = j - } else ++j - } - if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)) - } - } - if (direction == "ltr") { - if (order[0].level == 1 && (m = str.match(/^\s+/))) { - order[0].from = m[0].length - order.unshift(new BidiSpan(0, 0, m[0].length)) - } - if (lst(order).level == 1 && (m = str.match(/\s+$/))) { - lst(order).to -= m[0].length - order.push(new BidiSpan(0, len - m[0].length, len)) - } - } - - return direction == "rtl" ? order.reverse() : order - } -})() - -// Get the bidi ordering for the given line (and cache it). Returns -// false for lines that are fully left-to-right, and an array of -// BidiSpan objects otherwise. -export function getOrder(line, direction) { - let order = line.order - if (order == null) order = line.order = bidiOrdering(line.text, direction) - return order -} diff --git a/CODE/js/codemirror/src/util/browser.js b/CODE/js/codemirror/src/util/browser.js deleted file mode 100644 index 9fc4602c..00000000 --- a/CODE/js/codemirror/src/util/browser.js +++ /dev/null @@ -1,33 +0,0 @@ -// Kludges for bugs and behavior differences that can't be feature -// detected are enabled based on userAgent etc sniffing. -let userAgent = navigator.userAgent -let platform = navigator.platform - -export let gecko = /gecko\/\d/i.test(userAgent) -let ie_upto10 = /MSIE \d/.test(userAgent) -let ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) -let edge = /Edge\/(\d+)/.exec(userAgent) -export let ie = ie_upto10 || ie_11up || edge -export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]) -export let webkit = !edge && /WebKit\//.test(userAgent) -let qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) -export let chrome = !edge && /Chrome\//.test(userAgent) -export let presto = /Opera\//.test(userAgent) -export let safari = /Apple Computer/.test(navigator.vendor) -export let mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) -export let phantom = /PhantomJS/.test(userAgent) - -export let ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) -export let android = /Android/.test(userAgent) -// This is woefully incomplete. Suggestions for alternative methods welcome. -export let mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) -export let mac = ios || /Mac/.test(platform) -export let chromeOS = /\bCrOS\b/.test(userAgent) -export let windows = /win/i.test(platform) - -let presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) -if (presto_version) presto_version = Number(presto_version[1]) -if (presto_version && presto_version >= 15) { presto = false; webkit = true } -// Some browsers use the wrong event properties to signal cmd/ctrl on OS X -export let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) -export let captureRightClick = gecko || (ie && ie_version >= 9) diff --git a/CODE/js/codemirror/src/util/dom.js b/CODE/js/codemirror/src/util/dom.js deleted file mode 100644 index 04d2569d..00000000 --- a/CODE/js/codemirror/src/util/dom.js +++ /dev/null @@ -1,97 +0,0 @@ -import { ie, ios } from "./browser.js" - -export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } - -export let rmClass = function(node, cls) { - let current = node.className - let match = classTest(cls).exec(current) - if (match) { - let after = current.slice(match.index + match[0].length) - node.className = current.slice(0, match.index) + (after ? match[1] + after : "") - } -} - -export function removeChildren(e) { - for (let count = e.childNodes.length; count > 0; --count) - e.removeChild(e.firstChild) - return e -} - -export function removeChildrenAndAdd(parent, e) { - return removeChildren(parent).appendChild(e) -} - -export function elt(tag, content, className, style) { - let e = document.createElement(tag) - if (className) e.className = className - if (style) e.style.cssText = style - if (typeof content == "string") e.appendChild(document.createTextNode(content)) - else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i]) - return e -} -// wrapper for elt, which removes the elt from the accessibility tree -export function eltP(tag, content, className, style) { - let e = elt(tag, content, className, style) - e.setAttribute("role", "presentation") - return e -} - -export let range -if (document.createRange) range = function(node, start, end, endNode) { - let r = document.createRange() - r.setEnd(endNode || node, end) - r.setStart(node, start) - return r -} -else range = function(node, start, end) { - let r = document.body.createTextRange() - try { r.moveToElementText(node.parentNode) } - catch(e) { return r } - r.collapse(true) - r.moveEnd("character", end) - r.moveStart("character", start) - return r -} - -export function contains(parent, child) { - if (child.nodeType == 3) // Android browser always returns false when child is a textnode - child = child.parentNode - if (parent.contains) - return parent.contains(child) - do { - if (child.nodeType == 11) child = child.host - if (child == parent) return true - } while (child = child.parentNode) -} - -export function activeElt() { - // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. - // IE < 10 will throw when accessed while the page is loading or in an iframe. - // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. - let activeElement - try { - activeElement = document.activeElement - } catch(e) { - activeElement = document.body || null - } - while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) - activeElement = activeElement.shadowRoot.activeElement - return activeElement -} - -export function addClass(node, cls) { - let current = node.className - if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls -} -export function joinClasses(a, b) { - let as = a.split(" ") - for (let i = 0; i < as.length; i++) - if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i] - return b -} - -export let selectInput = function(node) { node.select() } -if (ios) // Mobile Safari apparently has a bug where select() is broken. - selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } -else if (ie) // Suppress mysterious IE10 errors - selectInput = function(node) { try { node.select() } catch(_e) {} } diff --git a/CODE/js/codemirror/src/util/event.js b/CODE/js/codemirror/src/util/event.js deleted file mode 100644 index 4b6c7705..00000000 --- a/CODE/js/codemirror/src/util/event.js +++ /dev/null @@ -1,103 +0,0 @@ -import { mac } from "./browser.js" -import { indexOf } from "./misc.js" - -// EVENT HANDLING - -// Lightweight event framework. on/off also work on DOM nodes, -// registering native DOM handlers. - -const noHandlers = [] - -export let on = function(emitter, type, f) { - if (emitter.addEventListener) { - emitter.addEventListener(type, f, false) - } else if (emitter.attachEvent) { - emitter.attachEvent("on" + type, f) - } else { - let map = emitter._handlers || (emitter._handlers = {}) - map[type] = (map[type] || noHandlers).concat(f) - } -} - -export function getHandlers(emitter, type) { - return emitter._handlers && emitter._handlers[type] || noHandlers -} - -export function off(emitter, type, f) { - if (emitter.removeEventListener) { - emitter.removeEventListener(type, f, false) - } else if (emitter.detachEvent) { - emitter.detachEvent("on" + type, f) - } else { - let map = emitter._handlers, arr = map && map[type] - if (arr) { - let index = indexOf(arr, f) - if (index > -1) - map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) - } - } -} - -export function signal(emitter, type /*, values...*/) { - let handlers = getHandlers(emitter, type) - if (!handlers.length) return - let args = Array.prototype.slice.call(arguments, 2) - for (let i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) -} - -// The DOM events that CodeMirror handles can be overridden by -// registering a (non-DOM) handler on the editor for the event name, -// and preventDefault-ing the event in that handler. -export function signalDOMEvent(cm, e, override) { - if (typeof e == "string") - e = {type: e, preventDefault: function() { this.defaultPrevented = true }} - signal(cm, override || e.type, cm, e) - return e_defaultPrevented(e) || e.codemirrorIgnore -} - -export function signalCursorActivity(cm) { - let arr = cm._handlers && cm._handlers.cursorActivity - if (!arr) return - let set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) - for (let i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) - set.push(arr[i]) -} - -export function hasHandler(emitter, type) { - return getHandlers(emitter, type).length > 0 -} - -// Add on and off methods to a constructor's prototype, to make -// registering events on such objects more convenient. -export function eventMixin(ctor) { - ctor.prototype.on = function(type, f) {on(this, type, f)} - ctor.prototype.off = function(type, f) {off(this, type, f)} -} - -// Due to the fact that we still support jurassic IE versions, some -// compatibility wrappers are needed. - -export function e_preventDefault(e) { - if (e.preventDefault) e.preventDefault() - else e.returnValue = false -} -export function e_stopPropagation(e) { - if (e.stopPropagation) e.stopPropagation() - else e.cancelBubble = true -} -export function e_defaultPrevented(e) { - return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false -} -export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)} - -export function e_target(e) {return e.target || e.srcElement} -export function e_button(e) { - let b = e.which - if (b == null) { - if (e.button & 1) b = 1 - else if (e.button & 2) b = 3 - else if (e.button & 4) b = 2 - } - if (mac && e.ctrlKey && b == 1) b = 3 - return b -} diff --git a/CODE/js/codemirror/src/util/feature_detection.js b/CODE/js/codemirror/src/util/feature_detection.js deleted file mode 100644 index c33734eb..00000000 --- a/CODE/js/codemirror/src/util/feature_detection.js +++ /dev/null @@ -1,84 +0,0 @@ -import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom.js" -import { ie, ie_version } from "./browser.js" - -// Detect drag-and-drop -export let dragAndDrop = function() { - // There is *some* kind of drag-and-drop support in IE6-8, but I - // couldn't get it to work yet. - if (ie && ie_version < 9) return false - let div = elt('div') - return "draggable" in div || "dragDrop" in div -}() - -let zwspSupported -export function zeroWidthElement(measure) { - if (zwspSupported == null) { - let test = elt("span", "\u200b") - removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])) - if (measure.firstChild.offsetHeight != 0) - zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) - } - let node = zwspSupported ? elt("span", "\u200b") : - elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px") - node.setAttribute("cm-text", "") - return node -} - -// Feature-detect IE's crummy client rect reporting for bidi text -let badBidiRects -export function hasBadBidiRects(measure) { - if (badBidiRects != null) return badBidiRects - let txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) - let r0 = range(txt, 0, 1).getBoundingClientRect() - let r1 = range(txt, 1, 2).getBoundingClientRect() - removeChildren(measure) - if (!r0 || r0.left == r0.right) return false // Safari returns null in some cases (#2780) - return badBidiRects = (r1.right - r0.right < 3) -} - -// See if "".split is the broken IE version, if so, provide an -// alternative way to split lines. -export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? string => { - let pos = 0, result = [], l = string.length - while (pos <= l) { - let nl = string.indexOf("\n", pos) - if (nl == -1) nl = string.length - let line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) - let rt = line.indexOf("\r") - if (rt != -1) { - result.push(line.slice(0, rt)) - pos += rt + 1 - } else { - result.push(line) - pos = nl + 1 - } - } - return result -} : string => string.split(/\r\n?|\n/) - -export let hasSelection = window.getSelection ? te => { - try { return te.selectionStart != te.selectionEnd } - catch(e) { return false } -} : te => { - let range - try {range = te.ownerDocument.selection.createRange()} - catch(e) {} - if (!range || range.parentElement() != te) return false - return range.compareEndPoints("StartToEnd", range) != 0 -} - -export let hasCopyEvent = (() => { - let e = elt("div") - if ("oncopy" in e) return true - e.setAttribute("oncopy", "return;") - return typeof e.oncopy == "function" -})() - -let badZoomedRects = null -export function hasBadZoomedRects(measure) { - if (badZoomedRects != null) return badZoomedRects - let node = removeChildrenAndAdd(measure, elt("span", "x")) - let normal = node.getBoundingClientRect() - let fromRange = range(node, 0, 1).getBoundingClientRect() - return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 -} diff --git a/CODE/js/codemirror/src/util/misc.js b/CODE/js/codemirror/src/util/misc.js deleted file mode 100644 index 6dc8d861..00000000 --- a/CODE/js/codemirror/src/util/misc.js +++ /dev/null @@ -1,168 +0,0 @@ -export function bind(f) { - let args = Array.prototype.slice.call(arguments, 1) - return function(){return f.apply(null, args)} -} - -export function copyObj(obj, target, overwrite) { - if (!target) target = {} - for (let prop in obj) - if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) - target[prop] = obj[prop] - return target -} - -// Counts the column offset in a string, taking tabs into account. -// Used mostly to find indentation. -export function countColumn(string, end, tabSize, startIndex, startValue) { - if (end == null) { - end = string.search(/[^\s\u00a0]/) - if (end == -1) end = string.length - } - for (let i = startIndex || 0, n = startValue || 0;;) { - let nextTab = string.indexOf("\t", i) - if (nextTab < 0 || nextTab >= end) - return n + (end - i) - n += nextTab - i - n += tabSize - (n % tabSize) - i = nextTab + 1 - } -} - -export class Delayed { - constructor() { - this.id = null - this.f = null - this.time = 0 - this.handler = bind(this.onTimeout, this) - } - onTimeout(self) { - self.id = 0 - if (self.time <= +new Date) { - self.f() - } else { - setTimeout(self.handler, self.time - +new Date) - } - } - set(ms, f) { - this.f = f - const time = +new Date + ms - if (!this.id || time < this.time) { - clearTimeout(this.id) - this.id = setTimeout(this.handler, ms) - this.time = time - } - } -} - -export function indexOf(array, elt) { - for (let i = 0; i < array.length; ++i) - if (array[i] == elt) return i - return -1 -} - -// Number of pixels added to scroller and sizer to hide scrollbar -export let scrollerGap = 50 - -// Returned or thrown by various protocols to signal 'I'm not -// handling this'. -export let Pass = {toString: function(){return "CodeMirror.Pass"}} - -// Reused option objects for setSelection & friends -export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"} - -// The inverse of countColumn -- find the offset that corresponds to -// a particular column. -export function findColumn(string, goal, tabSize) { - for (let pos = 0, col = 0;;) { - let nextTab = string.indexOf("\t", pos) - if (nextTab == -1) nextTab = string.length - let skipped = nextTab - pos - if (nextTab == string.length || col + skipped >= goal) - return pos + Math.min(skipped, goal - col) - col += nextTab - pos - col += tabSize - (col % tabSize) - pos = nextTab + 1 - if (col >= goal) return pos - } -} - -let spaceStrs = [""] -export function spaceStr(n) { - while (spaceStrs.length <= n) - spaceStrs.push(lst(spaceStrs) + " ") - return spaceStrs[n] -} - -export function lst(arr) { return arr[arr.length-1] } - -export function map(array, f) { - let out = [] - for (let i = 0; i < array.length; i++) out[i] = f(array[i], i) - return out -} - -export function insertSorted(array, value, score) { - let pos = 0, priority = score(value) - while (pos < array.length && score(array[pos]) <= priority) pos++ - array.splice(pos, 0, value) -} - -function nothing() {} - -export function createObj(base, props) { - let inst - if (Object.create) { - inst = Object.create(base) - } else { - nothing.prototype = base - inst = new nothing() - } - if (props) copyObj(props, inst) - return inst -} - -let nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ -export function isWordCharBasic(ch) { - return /\w/.test(ch) || ch > "\x80" && - (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) -} -export function isWordChar(ch, helper) { - if (!helper) return isWordCharBasic(ch) - if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true - return helper.test(ch) -} - -export function isEmpty(obj) { - for (let n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false - return true -} - -// Extending unicode characters. A series of a non-extending char + -// any number of extending chars is treated as a single unit as far -// as editing and measuring is concerned. This is not fully correct, -// since some scripts/fonts/browsers also treat other configurations -// of code points as a group. -let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ -export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } - -// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. -export function skipExtendingChars(str, pos, dir) { - while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) pos += dir - return pos -} - -// Returns the value from the range [`from`; `to`] that satisfies -// `pred` and is closest to `from`. Assumes that at least `to` -// satisfies `pred`. Supports `from` being greater than `to`. -export function findFirst(pred, from, to) { - // At any point we are certain `to` satisfies `pred`, don't know - // whether `from` does. - let dir = from > to ? -1 : 1 - for (;;) { - if (from == to) return from - let midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF) - if (mid == from) return pred(mid) ? from : to - if (pred(mid)) to = mid - else from = mid + dir - } -} diff --git a/CODE/js/codemirror/src/util/operation_group.js b/CODE/js/codemirror/src/util/operation_group.js deleted file mode 100644 index f6815949..00000000 --- a/CODE/js/codemirror/src/util/operation_group.js +++ /dev/null @@ -1,72 +0,0 @@ -import { getHandlers } from "./event.js" - -let operationGroup = null - -export function pushOperation(op) { - if (operationGroup) { - operationGroup.ops.push(op) - } else { - op.ownsGroup = operationGroup = { - ops: [op], - delayedCallbacks: [] - } - } -} - -function fireCallbacksForOps(group) { - // Calls delayed callbacks and cursorActivity handlers until no - // new ones appear - let callbacks = group.delayedCallbacks, i = 0 - do { - for (; i < callbacks.length; i++) - callbacks[i].call(null) - for (let j = 0; j < group.ops.length; j++) { - let op = group.ops[j] - if (op.cursorActivityHandlers) - while (op.cursorActivityCalled < op.cursorActivityHandlers.length) - op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) - } - } while (i < callbacks.length) -} - -export function finishOperation(op, endCb) { - let group = op.ownsGroup - if (!group) return - - try { fireCallbacksForOps(group) } - finally { - operationGroup = null - endCb(group) - } -} - -let orphanDelayedCallbacks = null - -// Often, we want to signal events at a point where we are in the -// middle of some work, but don't want the handler to start calling -// other methods on the editor, which might be in an inconsistent -// state or simply not expect any other events to happen. -// signalLater looks whether there are any handlers, and schedules -// them to be executed when the last operation ends, or, if no -// operation is active, when a timeout fires. -export function signalLater(emitter, type /*, values...*/) { - let arr = getHandlers(emitter, type) - if (!arr.length) return - let args = Array.prototype.slice.call(arguments, 2), list - if (operationGroup) { - list = operationGroup.delayedCallbacks - } else if (orphanDelayedCallbacks) { - list = orphanDelayedCallbacks - } else { - list = orphanDelayedCallbacks = [] - setTimeout(fireOrphanDelayed, 0) - } - for (let i = 0; i < arr.length; ++i) - list.push(() => arr[i].apply(null, args)) -} - -function fireOrphanDelayed() { - let delayed = orphanDelayedCallbacks - orphanDelayedCallbacks = null - for (let i = 0; i < delayed.length; ++i) delayed[i]() -} diff --git a/CODE/js/codemirror/test/annotatescrollbar.js b/CODE/js/codemirror/test/annotatescrollbar.js deleted file mode 100644 index 4a4d0533..00000000 --- a/CODE/js/codemirror/test/annotatescrollbar.js +++ /dev/null @@ -1,55 +0,0 @@ -namespace = "annotatescrollbar_"; - -(function () { - function test(name, run, content, query, expected) { - return testCM(name, function (cm) { - var annotation = cm.annotateScrollbar({ - listenForChanges: false, - className: "CodeMirror-search-match" - }); - var matches = []; - var cursor = cm.getSearchCursor(query, CodeMirror.Pos(0, 0)); - while (cursor.findNext()) { - var match = { - from: cursor.from(), - to: cursor.to() - }; - matches.push(match) - } - - if (run) run(cm); - - cm.display.barWidth = 5; - annotation.update(matches); - - var annotations = cm.getWrapperElement().getElementsByClassName(annotation.options.className); - eq(annotations.length, expected, "Expected " + expected + " annotations on the scrollbar.") - }, { - value: content, - mode: "javascript", - foldOptions: { - rangeFinder: CodeMirror.fold.brace - } - }); - } - - function doFold(cm) { - cm.foldCode(cm.getCursor()); - } - var simpleProg = "function foo() {\n\n return \"foo\";\n\n}\n\nfoo();\n"; - var consecutiveLineMatches = "function foo() {\n return \"foo\";\n}\nfoo();\n"; - var singleLineMatches = "function foo() { return \"foo\"; }foo();\n"; - - // Base case - expect 3 matches and 3 annotations - test("simple", null, simpleProg, "foo", 3); - // Consecutive line matches are combines into a single annotation - expect 3 matches and 2 annotations - test("combineConsecutiveLine", null, consecutiveLineMatches, "foo", 2); - // Matches on a single line get a single annotation - expect 3 matches and 1 annotation - test("combineSingleLine", null, singleLineMatches, "foo", 1); - // Matches within a fold are annotated on the folded line - expect 3 matches and 2 annotations - test("simpleFold", doFold, simpleProg, "foo", 2); - // Combination of combineConsecutiveLine and simpleFold cases - expect 3 matches and 1 annotation - test("foldedMatch", doFold, consecutiveLineMatches, "foo", 1); - // Hidden matches within a fold are annotated on the folded line - expect 1 match and 1 annotation - test("hiddenMatch", doFold, simpleProg, "return", 1); -})(); \ No newline at end of file diff --git a/CODE/js/codemirror/test/comment_test.js b/CODE/js/codemirror/test/comment_test.js deleted file mode 100644 index c6b9fe81..00000000 --- a/CODE/js/codemirror/test/comment_test.js +++ /dev/null @@ -1,114 +0,0 @@ -namespace = "comment_"; - -(function() { - function test(name, mode, run, before, after) { - return testCM(name, function(cm) { - run(cm); - eq(cm.getValue(), after); - }, {value: before, mode: mode}); - } - - var simpleProg = "function foo() {\n return bar;\n}"; - var inlineBlock = "foo(/* bar */ true);"; - var inlineBlocks = "foo(/* bar */ true, /* baz */ false);"; - var multiLineInlineBlock = ["above();", "foo(/* bar */ true);", "below();"]; - - test("block", "javascript", function(cm) { - cm.blockComment(Pos(0, 3), Pos(3, 0), {blockCommentLead: " *"}); - }, simpleProg + "\n", "/* function foo() {\n * return bar;\n * }\n */"); - - test("blockToggle", "javascript", function(cm) { - cm.blockComment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); - cm.uncomment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); - }, simpleProg, simpleProg); - - test("blockToggle2", "javascript", function(cm) { - cm.setCursor({line: 0, ch: 7 /* inside the block comment */}); - cm.execCommand("toggleComment"); - }, inlineBlock, "foo(bar true);"); - - // This test should work but currently fails. - // test("blockToggle3", "javascript", function(cm) { - // cm.setCursor({line: 0, ch: 7 /* inside the first block comment */}); - // cm.execCommand("toggleComment"); - // }, inlineBlocks, "foo(bar true, /* baz */ false);"); - - test("line", "javascript", function(cm) { - cm.lineComment(Pos(1, 1), Pos(1, 1)); - }, simpleProg, "function foo() {\n// return bar;\n}"); - - test("lineToggle", "javascript", function(cm) { - cm.lineComment(Pos(0, 0), Pos(2, 1)); - cm.uncomment(Pos(0, 0), Pos(2, 1)); - }, simpleProg, simpleProg); - - test("fallbackToBlock", "css", function(cm) { - cm.lineComment(Pos(0, 0), Pos(2, 1)); - }, "html {\n border: none;\n}", "/* html {\n border: none;\n} */"); - - test("fallbackToLine", "ruby", function(cm) { - cm.blockComment(Pos(0, 0), Pos(1)); - }, "def blah()\n return hah\n", "# def blah()\n# return hah\n"); - - test("ignoreExternalBlockComments", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, inlineBlocks, "// " + inlineBlocks); - - test("ignoreExternalBlockComments2", "javascript", function(cm) { - cm.setCursor({line: 0, ch: null /* eol */}); - cm.execCommand("toggleComment"); - }, inlineBlocks, "// " + inlineBlocks); - - test("ignoreExternalBlockCommentsMultiLineAbove", "javascript", function(cm) { - cm.setSelection({line: 0, ch: 0}, {line: 1, ch: 1}); - cm.execCommand("toggleComment"); - }, multiLineInlineBlock.join("\n"), ["// " + multiLineInlineBlock[0], - "// " + multiLineInlineBlock[1], - multiLineInlineBlock[2]].join("\n")); - - test("ignoreExternalBlockCommentsMultiLineBelow", "javascript", function(cm) { - cm.setSelection({line: 1, ch: 13 /* after end of block comment */}, {line: 2, ch: 1}); - cm.execCommand("toggleComment"); - }, multiLineInlineBlock.join("\n"), [multiLineInlineBlock[0], - "// " + multiLineInlineBlock[1], - "// " + multiLineInlineBlock[2]].join("\n")); - - test("commentRange", "javascript", function(cm) { - cm.blockComment(Pos(1, 2), Pos(1, 13), {fullLines: false}); - }, simpleProg, "function foo() {\n /*return bar;*/\n}"); - - test("indented", "javascript", function(cm) { - cm.lineComment(Pos(1, 0), Pos(2), {indent: true}); - }, simpleProg, "function foo() {\n// return bar;\n// }"); - - test("singleEmptyLine", "javascript", function(cm) { - cm.setCursor(1); - cm.execCommand("toggleComment"); - }, "a;\n\nb;", "a;\n// \nb;"); - - test("dontMessWithStrings", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");"); - - test("dontMessWithStrings2", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "console.log(\"// string\");", "// console.log(\"// string\");"); - - test("dontMessWithStrings3", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "// console.log(\"// string\");", "console.log(\"// string\");"); - - test("includeLastLine", "javascript", function(cm) { - cm.execCommand("selectAll") - cm.execCommand("toggleComment") - }, "// foo\n// bar\nbaz", "// // foo\n// // bar\n// baz") - - test("uncommentWithTrailingBlockEnd", "xml", function(cm) { - cm.execCommand("toggleComment") - }, " -->", "foo -->") - - test("dontCommentInComment", "xml", function(cm) { - cm.setCursor(1, 0) - cm.execCommand("toggleComment") - }, "", "") -})(); diff --git a/CODE/js/codemirror/test/contenteditable_test.js b/CODE/js/codemirror/test/contenteditable_test.js deleted file mode 100644 index 9130fa49..00000000 --- a/CODE/js/codemirror/test/contenteditable_test.js +++ /dev/null @@ -1,110 +0,0 @@ -(function() { - "use strict"; - - namespace = "contenteditable_"; - var Pos = CodeMirror.Pos - - function findTextNode(dom, text) { - if (dom instanceof CodeMirror) dom = dom.getInputField() - if (dom.nodeType == 1) { - for (var ch = dom.firstChild; ch; ch = ch.nextSibling) { - var found = findTextNode(ch, text) - if (found) return found - } - } else if (dom.nodeType == 3 && dom.nodeValue == text) { - return dom - } - } - - function lineElt(node) { - for (;;) { - var parent = node.parentNode - if (/CodeMirror-code/.test(parent.className)) return node - node = parent - } - } - - testCM("insert_text", function(cm) { - findTextNode(cm, "foobar").nodeValue = "foo bar" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "foo bar") - }, {inputStyle: "contenteditable", value: "foobar"}) - - testCM("split_line", function(cm) { - cm.setSelection(Pos(2, 3)) - var node = findTextNode(cm, "foobar") - node.nodeValue = "foo" - var lineNode = lineElt(node) - lineNode.parentNode.insertBefore(document.createElement("pre"), lineNode.nextSibling).textContent = "bar" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "one\ntwo\nfoo\nbar\nthree\nfour\n") - }, {inputStyle: "contenteditable", value: "one\ntwo\nfoobar\nthree\nfour\n"}) - - testCM("join_line", function(cm) { - cm.setSelection(Pos(2, 3)) - var node = findTextNode(cm, "foo") - node.nodeValue = "foobar" - var lineNode = lineElt(node) - lineNode.parentNode.removeChild(lineNode.nextSibling) - cm.display.input.updateFromDOM() - eq(cm.getValue(), "one\ntwo\nfoobar\nthree\nfour\n") - }, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"}) - - testCM("delete_multiple", function(cm) { - cm.setSelection(Pos(1, 3), Pos(4, 0)) - var text = findTextNode(cm, "two"), startLine = lineElt(text) - for (var i = 0; i < 3; i++) - startLine.parentNode.removeChild(startLine.nextSibling) - text.nodeValue = "twothree" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "one\ntwothree\nfour\n") - }, {inputStyle: "contenteditable", value: "one\ntwo\nfoo\nbar\nthree\nfour\n"}) - - testCM("ambiguous_diff_middle", function(cm) { - cm.setSelection(Pos(0, 2)) - findTextNode(cm, "baah").nodeValue = "baaah" - cm.display.input.updateFromDOM() - eqCharPos(cm.getCursor(), Pos(0, 3)) - }, {inputStyle: "contenteditable", value: "baah"}) - - testCM("ambiguous_diff_start", function(cm) { - cm.setSelection(Pos(0, 1)) - findTextNode(cm, "baah").nodeValue = "baaah" - cm.display.input.updateFromDOM() - eqCharPos(cm.getCursor(), Pos(0, 2)) - }, {inputStyle: "contenteditable", value: "baah"}) - - testCM("ambiguous_diff_end", function(cm) { - cm.setSelection(Pos(0, 3)) - findTextNode(cm, "baah").nodeValue = "baaah" - cm.display.input.updateFromDOM() - eqCharPos(cm.getCursor(), Pos(0, 4)) - }, {inputStyle: "contenteditable", value: "baah"}) - - testCM("force_redraw", function(cm) { - findTextNode(cm, "foo").parentNode.appendChild(document.createElement("hr")).className = "inserted" - cm.display.input.updateFromDOM() - eq(byClassName(cm.getInputField(), "inserted").length, 0) - }, {inputStyle: "contenteditable", value: "foo"}) - - testCM("type_on_empty_line", function(cm) { - cm.setSelection(Pos(1, 0)) - findTextNode(cm, "\u200b").nodeValue += "hello" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "foo\nhello\nbar") - }, {inputStyle: "contenteditable", value: "foo\n\nbar"}) - - testCM("type_after_empty_line", function(cm) { - cm.setSelection(Pos(2, 0)) - findTextNode(cm, "bar").nodeValue = "hellobar" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "foo\n\nhellobar") - }, {inputStyle: "contenteditable", value: "foo\n\nbar"}) - - testCM("type_before_empty_line", function(cm) { - cm.setSelection(Pos(0, 3)) - findTextNode(cm, "foo").nodeValue = "foohello" - cm.display.input.updateFromDOM() - eq(cm.getValue(), "foohello\n\nbar") - }, {inputStyle: "contenteditable", value: "foo\n\nbar"}) -})(); diff --git a/CODE/js/codemirror/test/doc_test.js b/CODE/js/codemirror/test/doc_test.js deleted file mode 100644 index 3af20ff9..00000000 --- a/CODE/js/codemirror/test/doc_test.js +++ /dev/null @@ -1,371 +0,0 @@ -(function() { - // A minilanguage for instantiating linked CodeMirror instances and Docs - function instantiateSpec(spec, place, opts) { - var names = {}, pos = 0, l = spec.length, editors = []; - while (spec) { - var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); - var name = m[1], isDoc = m[2], cur; - if (m[3]) { - cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); - } else { - var other = m[5]; - if (!names.hasOwnProperty(other)) { - names[other] = editors.length; - editors.push(CodeMirror(place, opts)); - } - var doc = editors[names[other]].linkedDoc({ - sharedHist: !m[4], - from: m[6] ? Number(m[6]) : null, - to: m[7] ? Number(m[7]) : null - }); - cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); - } - names[name] = editors.length; - editors.push(cur); - spec = spec.slice(m[0].length); - } - return editors; - } - - function clone(obj, props) { - if (!obj) return; - clone.prototype = obj; - var inst = new clone(); - if (props) for (var n in props) if (props.hasOwnProperty(n)) - inst[n] = props[n]; - return inst; - } - - function eqAll(val) { - var end = arguments.length, msg = null; - if (typeof arguments[end-1] == "string") - msg = arguments[--end]; - if (i == end) throw new Error("No editors provided to eqAll"); - for (var i = 1; i < end; ++i) - eq(arguments[i].getValue(), val, msg) - } - - function testDoc(name, spec, run, opts, expectFail) { - if (!opts) opts = {}; - - return test("doc_" + name, function() { - var place = document.getElementById("testground"); - var editors = instantiateSpec(spec, place, opts); - var successful = false; - - try { - run.apply(null, editors); - successful = true; - } finally { - if (!successful || verbose) { - place.style.visibility = "visible"; - } else { - for (var i = 0; i < editors.length; ++i) - if (editors[i] instanceof CodeMirror) - place.removeChild(editors[i].getWrapperElement()); - } - } - }, expectFail); - } - - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - - function testBasic(a, b) { - eqAll("x", a, b); - a.setValue("hey"); - eqAll("hey", a, b); - b.setValue("wow"); - eqAll("wow", a, b); - a.replaceRange("u\nv\nw", Pos(0, 3)); - b.replaceRange("i", Pos(0, 4)); - b.replaceRange("j", Pos(2, 1)); - eqAll("wowui\nv\nwj", a, b); - } - - testDoc("basic", "A='x' B 0, "not at left"); - is(pos.top > 0, "not at top"); - }); - - testDoc("copyDoc", "A='u'", function(a) { - var copy = a.getDoc().copy(true); - a.setValue("foo"); - copy.setValue("bar"); - var old = a.swapDoc(copy); - eq(a.getValue(), "bar"); - a.undo(); - eq(a.getValue(), "u"); - a.swapDoc(old); - eq(a.getValue(), "foo"); - eq(old.historySize().undo, 1); - eq(old.copy(false).historySize().undo, 0); - }); - - testDoc("docKeepsMode", "A='1+1'", function(a) { - var other = CodeMirror.Doc("hi", "text/x-markdown"); - a.setOption("mode", "text/javascript"); - var old = a.swapDoc(other); - eq(a.getOption("mode"), "text/x-markdown"); - eq(a.getMode().name, "markdown"); - a.swapDoc(old); - eq(a.getOption("mode"), "text/javascript"); - eq(a.getMode().name, "javascript"); - }); - - testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { - eq(b.getValue(), "2\n3"); - eq(b.firstLine(), 1); - b.setCursor(Pos(4)); - eqCharPos(b.getCursor(), Pos(2, 1)); - a.replaceRange("-1\n0\n", Pos(0, 0)); - eq(b.firstLine(), 3); - eqCharPos(b.getCursor(), Pos(4, 1)); - a.undo(); - eqCharPos(b.getCursor(), Pos(2, 1)); - b.replaceRange("oyoy\n", Pos(2, 0)); - eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); - b.undo(); - eq(a.getValue(), "1\n2\n3\n4\n5"); - }); - - testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { - a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); - eq(b.firstLine(), 2); - eq(b.lineCount(), 2); - eq(b.getValue(), "z3\n44"); - a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); - eq(b.firstLine(), 2); - eq(b.getValue(), "z3\n4q"); - eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); - a.execCommand("selectAll"); - a.replaceSelection("!"); - eqAll("!", a, b); - }); - - - testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B 500){ - totalTime = 0; - delay = 50; - } - setTimeout(function(){step(i + 1);}, delay); - } else { // Quit tests - running = false; - return null; - } - } - step(0); -} - -function label(str, msg) { - if (msg) return str + " (" + msg + ")"; - return str; -} -function eq(a, b, msg) { - if (a != b) throw new Failure(label(a + " != " + b, msg)); -} -function near(a, b, margin, msg) { - if (Math.abs(a - b) > margin) - throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg)); -} -function eqCharPos(a, b, msg) { - function str(p) { return "{line:" + p.line + ",ch:" + p.ch + ",sticky:" + p.sticky + "}"; } - if (a == b) return; - if (a == null) throw new Failure(label("comparing null to " + str(b), msg)); - if (b == null) throw new Failure(label("comparing " + str(a) + " to null", msg)); - if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg)); -} -function eqCursorPos(a, b, msg) { - eqCharPos(a, b, msg); - if (a) eq(a.sticky, b.sticky, msg ? msg + ' (sticky)' : 'sticky'); -} -function is(a, msg) { - if (!a) throw new Failure(label("assertion failed", msg)); -} - -function countTests() { - if (!filters.length) return tests.length; - var sum = 0; - for (var i = 0; i < tests.length; ++i) { - var name = tests[i].name; - for (var j = 0; j < filters.length; j++) { - if (name.match(filters[j])) { - ++sum; - break; - } - } - } - return sum; -} - -function parseTestFilter(s) { - if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i"); - else return new RegExp(s, "i"); -} diff --git a/CODE/js/codemirror/test/emacs_test.js b/CODE/js/codemirror/test/emacs_test.js deleted file mode 100644 index 412dba4b..00000000 --- a/CODE/js/codemirror/test/emacs_test.js +++ /dev/null @@ -1,149 +0,0 @@ -(function() { - "use strict"; - - var Pos = CodeMirror.Pos; - namespace = "emacs_"; - - var eventCache = {}; - function fakeEvent(keyName) { - var event = eventCache[key]; - if (event) return event; - - var ctrl, shift, alt; - var key = keyName.replace(/\w+-/g, function(type) { - if (type == "Ctrl-") ctrl = true; - else if (type == "Alt-") alt = true; - else if (type == "Shift-") shift = true; - return ""; - }); - var code; - for (var c in CodeMirror.keyNames) - if (CodeMirror.keyNames[c] == key) { code = c; break; } - if (code == null) throw new Error("Unknown key: " + key); - - return eventCache[keyName] = { - type: "keydown", keyCode: code, ctrlKey: ctrl, shiftKey: shift, altKey: alt, - preventDefault: function(){}, stopPropagation: function(){} - }; - } - - function sim(name, start /*, actions... */) { - var keys = Array.prototype.slice.call(arguments, 2); - testCM(name, function(cm) { - for (var i = 0; i < keys.length; ++i) { - var cur = keys[i]; - if (cur instanceof Pos) cm.setCursor(cur); - else if (cur.call) cur(cm); - else cm.triggerOnKeyDown(fakeEvent(cur)); - } - }, {keyMap: "emacs", value: start, mode: "javascript"}); - } - - function at(line, ch, sticky) { return function(cm) { eqCursorPos(cm.getCursor(), Pos(line, ch, sticky)); }; } - function txt(str) { return function(cm) { eq(cm.getValue(), str); }; } - - sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1, "after")); - sim("motionHMulti", "abcde", - "Ctrl-4", "Ctrl-F", at(0, 4, "before"), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2, "after"), - "Ctrl-5", "Ctrl-B", at(0, 0, "after")); - - sim("motionHWord", "abc. def ghi", - "Alt-F", at(0, 3, "before"), "Alt-F", at(0, 8, "before"), - "Ctrl-B", "Alt-B", at(0, 5, "after"), "Alt-B", at(0, 0, "after")); - sim("motionHWordMulti", "abc. def ghi ", - "Ctrl-3", "Alt-F", at(0, 12, "before"), "Ctrl-2", "Alt-B", at(0, 5, "after"), - "Ctrl--", "Alt-B", at(0, 8, "before")); - - sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0, "after")); - sim("motionVMulti", "a\nb\nc\nd\ne\n", - "Ctrl-2", "Ctrl-N", at(2, 0, "after"), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1, "before"), - "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1, "before")); - - sim("killYank", "abc\ndef\nghi", - "Ctrl-F", "Ctrl-Space", "Ctrl-N", "Ctrl-N", "Ctrl-W", "Ctrl-E", "Ctrl-Y", - txt("ahibc\ndef\ng")); - sim("killRing", "abcdef", - "Ctrl-Space", "Ctrl-F", "Ctrl-W", "Ctrl-Space", "Ctrl-F", "Ctrl-W", - "Ctrl-Y", "Alt-Y", - txt("acdef")); - sim("copyYank", "abcd", - "Ctrl-Space", "Ctrl-E", "Alt-W", "Ctrl-Y", - txt("abcdabcd")); - - sim("killLineSimple", "foo\nbar", "Ctrl-F", "Ctrl-K", txt("f\nbar")); - sim("killLineEmptyLine", "foo\n \nbar", "Ctrl-N", "Ctrl-K", txt("foo\nbar")); - sim("killLineMulti", "foo\nbar\nbaz", - "Ctrl-F", "Ctrl-F", "Ctrl-K", "Ctrl-K", "Ctrl-K", "Ctrl-A", "Ctrl-Y", - txt("o\nbarfo\nbaz")); - - sim("moveByParagraph", "abc\ndef\n\n\nhij\nklm\n\n", - "Ctrl-F", "Ctrl-Down", at(2, 0), "Ctrl-Down", at(6, 0), - "Ctrl-N", "Ctrl-Up", at(3, 0), "Ctrl-Up", at(0, 0), - Pos(1, 2), "Ctrl-Down", at(2, 0), Pos(4, 2), "Ctrl-Up", at(3, 0)); - sim("moveByParagraphMulti", "abc\n\ndef\n\nhij\n\nklm", - "Ctrl-U", "2", "Ctrl-Down", at(3, 0), - "Shift-Alt-.", "Ctrl-3", "Ctrl-Up", at(1, 0)); - - sim("moveBySentence", "sentence one! sentence\ntwo\n\nparagraph two", - "Alt-E", at(0, 13), "Alt-E", at(1, 3), "Ctrl-F", "Alt-A", at(0, 13)); - - sim("moveByExpr", "function foo(a, b) {}", - "Ctrl-Alt-F", at(0, 8), "Ctrl-Alt-F", at(0, 12), "Ctrl-Alt-F", at(0, 18), - "Ctrl-Alt-B", at(0, 12), "Ctrl-Alt-B", at(0, 9)); - sim("moveByExprMulti", "foo bar baz bug", - "Ctrl-2", "Ctrl-Alt-F", at(0, 7), - "Ctrl--", "Ctrl-Alt-F", at(0, 4), - "Ctrl--", "Ctrl-2", "Ctrl-Alt-B", at(0, 11)); - sim("delExpr", "var x = [\n a,\n b\n c\n];", - Pos(0, 8), "Ctrl-Alt-K", txt("var x = ;"), "Ctrl-/", - Pos(4, 1), "Ctrl-Alt-Backspace", txt("var x = ;")); - sim("delExprMulti", "foo bar baz", - "Ctrl-2", "Ctrl-Alt-K", txt(" baz"), - "Ctrl-/", "Ctrl-E", "Ctrl-2", "Ctrl-Alt-Backspace", txt("foo ")); - - sim("justOneSpace", "hi bye ", - Pos(0, 4), "Alt-Space", txt("hi bye "), - Pos(0, 4), "Alt-Space", txt("hi b ye "), - "Ctrl-A", "Alt-Space", "Ctrl-E", "Alt-Space", txt(" hi b ye ")); - - sim("openLine", "foo bar", "Alt-F", "Ctrl-O", txt("foo\n bar")) - - sim("transposeChar", "abcd\ne", - "Ctrl-F", "Ctrl-T", "Ctrl-T", txt("bcad\ne"), at(0, 3), - "Ctrl-F", "Ctrl-T", "Ctrl-T", "Ctrl-T", txt("bcda\ne"), at(0, 4), - "Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 1)); - - sim("manipWordCase", "foo BAR bAZ", - "Alt-C", "Alt-L", "Alt-U", txt("Foo bar BAZ"), - "Ctrl-A", "Alt-U", "Alt-L", "Alt-C", txt("FOO bar Baz")); - sim("manipWordCaseMulti", "foo Bar bAz", - "Ctrl-2", "Alt-U", txt("FOO BAR bAz"), - "Ctrl-A", "Ctrl-3", "Alt-C", txt("Foo Bar Baz")); - - sim("upExpr", "foo {\n bar[];\n baz(blah);\n}", - Pos(2, 7), "Ctrl-Alt-U", at(2, 5), "Ctrl-Alt-U", at(0, 4)); - sim("transposeExpr", "do foo[bar] dah", - Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah")); - - sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F", - "Ctrl-G", "Ctrl-W", txt("abcde")); - - sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde")); - sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde")); - - sim("backspaceDoesntAddToRing", "foobar", "Ctrl-F", "Ctrl-F", "Ctrl-F", "Ctrl-K", "Backspace", "Backspace", "Ctrl-Y", txt("fbar")); - - testCM("save", function(cm) { - var saved = false; - CodeMirror.commands.save = function(cm) { saved = cm.getValue(); }; - cm.triggerOnKeyDown(fakeEvent("Ctrl-X")); - cm.triggerOnKeyDown(fakeEvent("Ctrl-S")); - is(saved, "hi"); - }, {value: "hi", keyMap: "emacs"}); - - testCM("gotoInvalidLineFloat", function(cm) { - cm.openDialog = function(_, cb) { cb("2.2"); }; - cm.triggerOnKeyDown(fakeEvent("Alt-G")); - cm.triggerOnKeyDown(fakeEvent("G")); - }, {value: "1\n2\n3\n4", keyMap: "emacs"}); -})(); diff --git a/CODE/js/codemirror/test/html-hint-test.js b/CODE/js/codemirror/test/html-hint-test.js deleted file mode 100644 index a40582e2..00000000 --- a/CODE/js/codemirror/test/html-hint-test.js +++ /dev/null @@ -1,83 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE - -(function() { - var Pos = CodeMirror.Pos; - - namespace = "html-hint_"; - - testData =[ - { - name: "html-element", - value: "\n"] - }, - { - name: "linkref-attribute", - value: "\n", - list: [""] - } - ]; - - function escapeHtmlList(o) { - return '' + - JSON.stringify(o.list,null,2) - .replace(//g, ">") + - '' - } - - function test(name, spec) { - testCM(name, function(cm) { - cm.setValue(spec.value); - cm.setCursor(spec.cursor); - var completion = CodeMirror.hint.html(cm); - if (!deepCompare(completion.list, spec.list)) - throw new Failure("Wrong completion results. Got" + - escapeHtmlList(completion) +" but expected" + - escapeHtmlList(spec)); - eqCharPos(completion.from, spec.from,'from-failed'); - eqCharPos(completion.to, spec.to, 'to-failed'); - }, { - value: spec.value, - mode: spec.mode || "text/html" - }); - } - - testData.forEach(function (value) { - // Use sane defaults - var lines = value.value.split(/\n/); - value.to = value.pos || Pos(lines.length-1, lines[lines.length-1].length); - value.from = value.from || Pos(lines.length-1,0); - value.cursor = value.cursor || value.to; - var name = value.name ||value.value; - test(name,value) - }); - - function deepCompare(a, b) { - if (a === b) return true; - if (!(a && typeof a === "object") || - !(b && typeof b === "object")) return false; - var array = a instanceof Array - if ((b instanceof Array) !== array) return false; - if (array) { - if (a.length !== b.length) return false; - for (var i = 0; i < a.length; i++) if (!deepCompare(a[i], b[i])) return false - } else { - for (var p in a) if (!(p in b) || !deepCompare(a[p], b[p])) return false; - for (var p in b) if (!(p in a)) return false - } - return true - } -})(); diff --git a/CODE/js/codemirror/test/index.html b/CODE/js/codemirror/test/index.html deleted file mode 100644 index 3369beac..00000000 --- a/CODE/js/codemirror/test/index.html +++ /dev/null @@ -1,291 +0,0 @@ - - - -CodeMirror: Test Suite - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Test Suite

- -

A limited set of programmatic sanity tests for CodeMirror.

- -
-
Ran 0 of 0 tests
-
-

Please enable JavaScript...

-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/CODE/js/codemirror/test/lint.js b/CODE/js/codemirror/test/lint.js deleted file mode 100644 index bdbed1a8..00000000 --- a/CODE/js/codemirror/test/lint.js +++ /dev/null @@ -1,20 +0,0 @@ -var blint = require("blint"); - -["mode", "lib", "addon", "keymap"].forEach(function(dir) { - blint.checkDir(dir, { - browser: true, - allowedGlobals: ["CodeMirror", "define", "test", "requirejs", "globalThis"], - ecmaVersion: 5, - tabs: dir == "lib" - }); -}); - -["src"].forEach(function(dir) { - blint.checkDir(dir, { - browser: true, - ecmaVersion: 6, - semicolons: false - }); -}); - -module.exports = {ok: blint.success()}; diff --git a/CODE/js/codemirror/test/mode_test.css b/CODE/js/codemirror/test/mode_test.css deleted file mode 100644 index f83271b4..00000000 --- a/CODE/js/codemirror/test/mode_test.css +++ /dev/null @@ -1,23 +0,0 @@ -.mt-output .mt-token { - border: 1px solid #ddd; - white-space: pre; - font-family: "Consolas", monospace; - text-align: center; -} - -.mt-output .mt-style { - font-size: x-small; -} - -.mt-output .mt-state { - font-size: x-small; - vertical-align: top; -} - -.mt-output .mt-state-row { - display: none; -} - -.mt-state-unhide .mt-output .mt-state-row { - display: table-row; -} diff --git a/CODE/js/codemirror/test/mode_test.js b/CODE/js/codemirror/test/mode_test.js deleted file mode 100644 index e7c0cf92..00000000 --- a/CODE/js/codemirror/test/mode_test.js +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Helper to test CodeMirror highlighting modes. It pretty prints output of the - * highlighter and can check against expected styles. - * - * Mode tests are registered by calling test.mode(testName, mode, - * tokens), where mode is a mode object as returned by - * CodeMirror.getMode, and tokens is an array of lines that make up - * the test. - * - * These lines are strings, in which styled stretches of code are - * enclosed in brackets `[]`, and prefixed by their style. For - * example, `[keyword if]`. Brackets in the code itself must be - * duplicated to prevent them from being interpreted as token - * boundaries. For example `a[[i]]` for `a[i]`. If a token has - * multiple styles, the styles must be separated by ampersands, for - * example `[tag&error ]`. - * - * See the test.js files in the css, markdown, gfm, and stex mode - * directories for examples. - */ -(function() { - function findSingle(str, pos, ch) { - for (;;) { - var found = str.indexOf(ch, pos); - if (found == -1) return null; - if (str.charAt(found + 1) != ch) return found; - pos = found + 2; - } - } - - var styleName = /[\w&-_]+/g; - function parseTokens(strs) { - var tokens = [], plain = ""; - for (var i = 0; i < strs.length; ++i) { - if (i) plain += "\n"; - var str = strs[i], pos = 0; - while (pos < str.length) { - var style = null, text; - if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") { - styleName.lastIndex = pos + 1; - var m = styleName.exec(str); - style = m[0].replace(/&/g, " "); - var textStart = pos + style.length + 2; - var end = findSingle(str, textStart, "]"); - if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style); - text = str.slice(textStart, end); - pos = end + 1; - } else { - var end = findSingle(str, pos, "["); - if (end == null) end = str.length; - text = str.slice(pos, end); - pos = end; - } - text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);}); - tokens.push({style: style, text: text}); - plain += text; - } - } - return {tokens: tokens, plain: plain}; - } - - test.mode = function(name, mode, tokens, modeName) { - var data = parseTokens(tokens); - return test((modeName || mode.name) + "_" + name, function() { - return compare(data.plain, data.tokens, mode); - }); - }; - - function esc(str) { - return str.replace(/&/g, '&').replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); - } - - function compare(text, expected, mode) { - - var expectedOutput = []; - for (var i = 0; i < expected.length; ++i) { - var sty = expected[i].style; - if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' '); - expectedOutput.push({style: sty, text: expected[i].text}); - } - - var observedOutput = highlight(text, mode); - - var s = ""; - var diff = highlightOutputsDifferent(expectedOutput, observedOutput); - if (diff != null) { - s += '
'; - s += '
' + esc(text) + '
'; - s += '
'; - s += 'expected:'; - s += prettyPrintOutputTable(expectedOutput, diff); - s += 'observed: [display states]'; - s += prettyPrintOutputTable(observedOutput, diff); - s += '
'; - s += '
'; - } - if (observedOutput.indentFailures) { - for (var i = 0; i < observedOutput.indentFailures.length; i++) - s += "
" + esc(observedOutput.indentFailures[i]) + "
"; - } - if (s) throw new Failure(s); - } - - function stringify(obj) { - function replacer(key, obj) { - if (typeof obj == "function") { - var m = obj.toString().match(/function\s*[^\s(]*/); - return m ? m[0] : "function"; - } - return obj; - } - if (window.JSON && JSON.stringify) - return JSON.stringify(obj, replacer, 2); - return "[unsupported]"; // Fail safely if no native JSON. - } - - function highlight(string, mode) { - var state = mode.startState(); - - var lines = string.replace(/\r\n/g,'\n').split('\n'); - var st = [], pos = 0; - for (var i = 0; i < lines.length; ++i) { - var line = lines[i], newLine = true; - if (mode.indent) { - var ws = line.match(/^\s*/)[0]; - var indent = mode.indent(state, line.slice(ws.length), line); - if (indent != CodeMirror.Pass && indent != ws.length) - (st.indentFailures || (st.indentFailures = [])).push( - "Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")"); - } - var stream = new CodeMirror.StringStream(line, 4, { - lookAhead: function(n) { return lines[i + n] } - }); - if (line == "" && mode.blankLine) mode.blankLine(state); - /* Start copied code from CodeMirror.highlight */ - while (!stream.eol()) { - for (var j = 0; j < 10 && stream.start >= stream.pos; j++) - var compare = mode.token(stream, state); - if (j == 10) - throw new Failure("Failed to advance the stream." + stream.string + " " + stream.pos); - var substr = stream.current(); - if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' '); - stream.start = stream.pos; - if (pos && st[pos-1].style == compare && !newLine) { - st[pos-1].text += substr; - } else if (substr) { - st[pos++] = {style: compare, text: substr, state: stringify(state)}; - } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = {style: null, text: this.text.slice(stream.pos)}; - break; - } - newLine = false; - } - } - - return st; - } - - function highlightOutputsDifferent(o1, o2) { - var minLen = Math.min(o1.length, o2.length); - for (var i = 0; i < minLen; ++i) - if (o1[i].style != o2[i].style || o1[i].text != o2[i].text) return i; - if (o1.length > minLen || o2.length > minLen) return minLen; - } - - function prettyPrintOutputTable(output, diffAt) { - var s = ''; - s += ''; - for (var i = 0; i < output.length; ++i) { - var style = output[i].style, val = output[i].text; - s += - ''; - } - s += ''; - for (var i = 0; i < output.length; ++i) { - s += ''; - } - if(output[0].state) { - s += ''; - for (var i = 0; i < output.length; ++i) { - s += ''; - } - } - s += '
' + - '' + - esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT - '' + - '
' + (output[i].style || null) + '
' + esc(output[i].state) + '
'; - return s; - } -})(); diff --git a/CODE/js/codemirror/test/multi_test.js b/CODE/js/codemirror/test/multi_test.js deleted file mode 100644 index cc042f73..00000000 --- a/CODE/js/codemirror/test/multi_test.js +++ /dev/null @@ -1,295 +0,0 @@ -(function() { - namespace = "multi_"; - - function hasSelections(cm) { - var sels = cm.listSelections(); - var given = (arguments.length - 1) / 4; - if (sels.length != given) - throw new Failure("expected " + given + " selections, found " + sels.length); - for (var i = 0, p = 1; i < given; i++, p += 4) { - var anchor = Pos(arguments[p], arguments[p + 1]); - var head = Pos(arguments[p + 2], arguments[p + 3]); - eqCharPos(sels[i].anchor, anchor, "anchor of selection " + i); - eqCharPos(sels[i].head, head, "head of selection " + i); - } - } - function hasCursors(cm) { - var sels = cm.listSelections(); - var given = (arguments.length - 1) / 2; - if (sels.length != given) - throw new Failure("expected " + given + " selections, found " + sels.length); - for (var i = 0, p = 1; i < given; i++, p += 2) { - eqCursorPos(sels[i].anchor, sels[i].head, "something selected for " + i); - var head = Pos(arguments[p], arguments[p + 1]); - eqCharPos(sels[i].head, head, "selection " + i); - } - } - - testCM("getSelection", function(cm) { - select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, {anchor: Pos(2, 2), head: Pos(2, 0)}); - eq(cm.getSelection(), "1234\n56\n90"); - eq(cm.getSelection(false).join("|"), "1234|56|90"); - eq(cm.getSelections().join("|"), "1234\n56|90"); - }, {value: "1234\n5678\n90"}); - - testCM("setSelection", function(cm) { - select(cm, Pos(3, 0), Pos(0, 0), {anchor: Pos(2, 5), head: Pos(1, 0)}); - hasSelections(cm, 0, 0, 0, 0, - 2, 5, 1, 0, - 3, 0, 3, 0); - cm.setSelection(Pos(1, 2), Pos(1, 1)); - hasSelections(cm, 1, 2, 1, 1); - select(cm, {anchor: Pos(1, 1), head: Pos(2, 4)}, - {anchor: Pos(0, 0), head: Pos(1, 3)}, - Pos(3, 0), Pos(2, 2)); - hasSelections(cm, 0, 0, 2, 4, - 3, 0, 3, 0); - cm.setSelections([{anchor: Pos(0, 1), head: Pos(0, 2)}, - {anchor: Pos(1, 1), head: Pos(1, 2)}, - {anchor: Pos(2, 1), head: Pos(2, 2)}], 1); - eqCharPos(cm.getCursor("head"), Pos(1, 2)); - eqCharPos(cm.getCursor("anchor"), Pos(1, 1)); - eqCharPos(cm.getCursor("from"), Pos(1, 1)); - eqCharPos(cm.getCursor("to"), Pos(1, 2)); - cm.setCursor(Pos(1, 1)); - hasCursors(cm, 1, 1); - }, {value: "abcde\nabcde\nabcde\n"}); - - testCM("somethingSelected", function(cm) { - select(cm, Pos(0, 1), {anchor: Pos(0, 3), head: Pos(0, 5)}); - eq(cm.somethingSelected(), true); - select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5)); - eq(cm.somethingSelected(), false); - }, {value: "123456789"}); - - testCM("extendSelection", function(cm) { - select(cm, Pos(0, 1), Pos(1, 1), Pos(2, 1)); - cm.setExtending(true); - cm.extendSelections([Pos(0, 2), Pos(1, 0), Pos(2, 3)]); - hasSelections(cm, 0, 1, 0, 2, - 1, 1, 1, 0, - 2, 1, 2, 3); - cm.extendSelection(Pos(2, 4), Pos(2, 0)); - hasSelections(cm, 2, 4, 2, 0); - }, {value: "1234\n1234\n1234"}); - - testCM("addSelection", function(cm) { - select(cm, Pos(0, 1), Pos(1, 1)); - cm.addSelection(Pos(0, 0), Pos(0, 4)); - hasSelections(cm, 0, 0, 0, 4, - 1, 1, 1, 1); - cm.addSelection(Pos(2, 2)); - hasSelections(cm, 0, 0, 0, 4, - 1, 1, 1, 1, - 2, 2, 2, 2); - }, {value: "1234\n1234\n1234"}); - - testCM("replaceSelection", function(cm) { - var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)}, - {anchor: Pos(0, 2), head: Pos(0, 3)}, - {anchor: Pos(0, 4), head: Pos(0, 5)}, - {anchor: Pos(2, 1), head: Pos(2, 4)}, - {anchor: Pos(2, 5), head: Pos(2, 6)}]; - var val = "123456\n123456\n123456"; - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("ab", "around"); - eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab"); - hasSelections(cm, 0, 0, 0, 2, - 0, 3, 0, 5, - 0, 6, 0, 8, - 2, 1, 2, 3, - 2, 4, 2, 6); - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("", "around"); - eq(cm.getValue(), "246\n123456\n15"); - hasSelections(cm, 0, 0, 0, 0, - 0, 1, 0, 1, - 0, 2, 0, 2, - 2, 1, 2, 1, - 2, 2, 2, 2); - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("X\nY\nZ", "around"); - hasSelections(cm, 0, 0, 2, 1, - 2, 2, 4, 1, - 4, 2, 6, 1, - 8, 1, 10, 1, - 10, 2, 12, 1); - cm.replaceSelection("a", "around"); - hasSelections(cm, 0, 0, 0, 1, - 0, 2, 0, 3, - 0, 4, 0, 5, - 2, 1, 2, 2, - 2, 3, 2, 4); - cm.replaceSelection("xy", "start"); - hasSelections(cm, 0, 0, 0, 0, - 0, 3, 0, 3, - 0, 6, 0, 6, - 2, 1, 2, 1, - 2, 4, 2, 4); - cm.replaceSelection("z\nf"); - hasSelections(cm, 1, 1, 1, 1, - 2, 1, 2, 1, - 3, 1, 3, 1, - 6, 1, 6, 1, - 7, 1, 7, 1); - eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy"); - }); - - function select(cm) { - var sels = []; - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - if (arg.head) sels.push(arg); - else sels.push({head: arg, anchor: arg}); - } - cm.setSelections(sels, sels.length - 1); - } - - testCM("indentSelection", function(cm) { - select(cm, Pos(0, 1), Pos(1, 1)); - cm.indentSelection(4); - eq(cm.getValue(), " foo\n bar\nbaz"); - - select(cm, Pos(0, 2), Pos(0, 3), Pos(0, 4)); - cm.indentSelection(-2); - eq(cm.getValue(), " foo\n bar\nbaz"); - - select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, - {anchor: Pos(1, 3), head: Pos(2, 0)}); - cm.indentSelection(-2); - eq(cm.getValue(), "foo\n bar\nbaz"); - }, {value: "foo\nbar\nbaz"}); - - testCM("killLine", function(cm) { - select(cm, Pos(0, 1), Pos(0, 2), Pos(1, 1)); - cm.execCommand("killLine"); - eq(cm.getValue(), "f\nb\nbaz"); - cm.execCommand("killLine"); - eq(cm.getValue(), "fbbaz"); - cm.setValue("foo\nbar\nbaz"); - select(cm, Pos(0, 1), {anchor: Pos(0, 2), head: Pos(2, 1)}); - cm.execCommand("killLine"); - eq(cm.getValue(), "faz"); - }, {value: "foo\nbar\nbaz"}); - - testCM("deleteLine", function(cm) { - select(cm, Pos(0, 0), - {head: Pos(0, 1), anchor: Pos(2, 0)}, - Pos(4, 0)); - cm.execCommand("deleteLine"); - eq(cm.getValue(), "4\n6\n7"); - select(cm, Pos(2, 1)); - cm.execCommand("deleteLine"); - eq(cm.getValue(), "4\n6\n"); - }, {value: "1\n2\n3\n4\n5\n6\n7"}); - - testCM("deleteH", function(cm) { - select(cm, Pos(0, 4), {anchor: Pos(1, 4), head: Pos(1, 5)}); - cm.execCommand("delWordAfter"); - eq(cm.getValue(), "foo bar baz\nabc ef ghi\n"); - cm.execCommand("delWordAfter"); - eq(cm.getValue(), "foo baz\nabc ghi\n"); - cm.execCommand("delCharBefore"); - cm.execCommand("delCharBefore"); - eq(cm.getValue(), "fo baz\nab ghi\n"); - select(cm, Pos(0, 3), Pos(0, 4), Pos(0, 5)); - cm.execCommand("delWordAfter"); - eq(cm.getValue(), "fo \nab ghi\n"); - }, {value: "foo bar baz\nabc def ghi\n"}); - - testCM("goLineStart", function(cm) { - select(cm, Pos(0, 2), Pos(0, 3), Pos(1, 1)); - cm.execCommand("goLineStart"); - hasCursors(cm, 0, 0, 1, 0); - select(cm, Pos(1, 1), Pos(0, 1)); - cm.setExtending(true); - cm.execCommand("goLineStart"); - hasSelections(cm, 0, 1, 0, 0, - 1, 1, 1, 0); - }, {value: "foo\nbar\nbaz"}); - - testCM("moveV", function(cm) { - select(cm, Pos(0, 2), Pos(1, 2)); - cm.execCommand("goLineDown"); - hasCursors(cm, 1, 2, 2, 2); - cm.execCommand("goLineUp"); - hasCursors(cm, 0, 2, 1, 2); - cm.execCommand("goLineUp"); - hasCursors(cm, 0, 0, 0, 2); - cm.execCommand("goLineUp"); - hasCursors(cm, 0, 0); - select(cm, Pos(0, 2), Pos(1, 2)); - cm.setExtending(true); - cm.execCommand("goLineDown"); - hasSelections(cm, 0, 2, 2, 2); - }, {value: "12345\n12345\n12345"}); - - testCM("moveH", function(cm) { - select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5), Pos(2, 3)); - cm.execCommand("goCharRight"); - hasCursors(cm, 0, 2, 0, 4, 1, 0, 2, 4); - cm.execCommand("goCharLeft"); - hasCursors(cm, 0, 1, 0, 3, 0, 5, 2, 3); - for (var i = 0; i < 15; i++) - cm.execCommand("goCharRight"); - hasCursors(cm, 2, 4, 2, 5); - }, {value: "12345\n12345\n12345"}); - - testCM("newlineAndIndent", function(cm) { - select(cm, Pos(0, 5), Pos(1, 5)); - cm.execCommand("newlineAndIndent"); - hasCursors(cm, 1, 2, 3, 2); - eq(cm.getValue(), "x = [\n 1];\ny = [\n 2];"); - cm.undo(); - eq(cm.getValue(), "x = [1];\ny = [2];"); - hasCursors(cm, 0, 5, 1, 5); - select(cm, Pos(0, 5), Pos(0, 6)); - cm.execCommand("newlineAndIndent"); - hasCursors(cm, 1, 2, 2, 0); - eq(cm.getValue(), "x = [\n 1\n];\ny = [2];"); - }, {value: "x = [1];\ny = [2];", mode: "javascript"}); - - testCM("goDocStartEnd", function(cm) { - select(cm, Pos(0, 1), Pos(1, 1)); - cm.execCommand("goDocStart"); - hasCursors(cm, 0, 0); - select(cm, Pos(0, 1), Pos(1, 1)); - cm.execCommand("goDocEnd"); - hasCursors(cm, 1, 3); - select(cm, Pos(0, 1), Pos(1, 1)); - cm.setExtending(true); - cm.execCommand("goDocEnd"); - hasSelections(cm, 1, 1, 1, 3); - }, {value: "abc\ndef"}); - - testCM("selectionHistory", function(cm) { - for (var i = 0; i < 3; ++i) - cm.addSelection(Pos(0, i * 2), Pos(0, i * 2 + 1)); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "1\n2"); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "1"); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), ""); - eqCharPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), "1"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), "1\n2"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), "1\n2\n3"); - }, {value: "1 2 3"}); - - testCM("selectionsMayTouch", function(cm) { - select(cm, Pos(0, 0), Pos(0, 2)) - cm.setExtending(true); - cm.extendSelections([Pos(0, 2), Pos(0, 4)]) - hasSelections(cm, 0, 0, 0, 2, - 0, 2, 0, 4) - cm.extendSelections([Pos(0, 3), Pos(0, 4)]) - hasSelections(cm, 0, 0, 0, 4) - }, {selectionsMayTouch: true, value: "1234"}) -})(); diff --git a/CODE/js/codemirror/test/run.js b/CODE/js/codemirror/test/run.js deleted file mode 100755 index 914cb976..00000000 --- a/CODE/js/codemirror/test/run.js +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env node - -var lint = require("./lint"); - -var files = new (require('node-static').Server)(); - -var server = require('http').createServer(function (req, res) { - req.addListener('end', function () { - files.serve(req, res, function (err/*, result */) { - if (err) { - console.error(err); - process.exit(1); - } - }); - }).resume(); -}).addListener('error', function (err) { - throw err; -}).listen(3000,(async () => { - const puppeteer = require('puppeteer'); - const browser = await puppeteer.launch({args: ["--no-sandbox", "--disable-setuid-sandbox"]}) - const page = await browser.newPage() - page.on('console', msg => console.log("console:", msg.text())) - page.on('dialog', async dialog => { - console.log(dialog.message()) - await dialog.dismiss() - }) - page.evaluateOnNewDocument(() => window.automatedTests = true) - await page.goto('http://localhost:3000/test/index.html#' + (process.argv[2] || "")) - while(1) { - if (await page.evaluate(() => window.done)) break - await sleep(200) - } - let [failed, errors] = await page.evaluate(() => [window.failed, window.errored]) - for (let error of errors) console.log(error) - console.log(await page.evaluate(() => document.getElementById('output').innerText + "\n" + - document.getElementById('status').innerText)) - process.exit(failed > 0 || errors.length || !lint.ok ? 1 : 0) - await browser.close() -})()) - -function sleep(n) { return new Promise(acc => setTimeout(acc, n)) } diff --git a/CODE/js/codemirror/test/scroll_test.js b/CODE/js/codemirror/test/scroll_test.js deleted file mode 100644 index d1d21900..00000000 --- a/CODE/js/codemirror/test/scroll_test.js +++ /dev/null @@ -1,126 +0,0 @@ -(function() { - "use strict"; - - namespace = "scroll_"; - - testCM("bars_hidden", function(cm) { - for (var i = 0;; i++) { - var wrapBox = cm.getWrapperElement().getBoundingClientRect(); - var scrollBox = cm.getScrollerElement().getBoundingClientRect(); - is(wrapBox.bottom < scrollBox.bottom - 10); - is(wrapBox.right < scrollBox.right - 10); - if (i == 1) break; - cm.getWrapperElement().style.height = "auto"; - cm.refresh(); - } - }); - - function barH(cm) { return byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; } - function barV(cm) { return byClassName(cm.getWrapperElement(), "CodeMirror-vscrollbar")[0]; } - - function displayBottom(cm, scrollbar) { - if (scrollbar && cm.display.scroller.offsetHeight > cm.display.scroller.clientHeight) - return barH(cm).getBoundingClientRect().top; - else - return cm.getWrapperElement().getBoundingClientRect().bottom - 1; - } - - function displayRight(cm, scrollbar) { - if (scrollbar && cm.display.scroller.offsetWidth > cm.display.scroller.clientWidth) - return barV(cm).getBoundingClientRect().left; - else - return cm.getWrapperElement().getBoundingClientRect().right - 1; - } - - function testMovedownFixed(cm, hScroll) { - cm.setSize("100px", "100px"); - if (hScroll) cm.setValue(new Array(100).join("x")); - var bottom = displayBottom(cm, hScroll); - for (var i = 0; i < 30; i++) { - cm.replaceSelection("x\n"); - var cursorBottom = cm.cursorCoords(null, "window").bottom; - is(cursorBottom <= bottom); - } - is(cursorBottom >= bottom - 5); - } - - testCM("movedown_fixed", function(cm) {testMovedownFixed(cm, false);}); - testCM("movedown_hscroll_fixed", function(cm) {testMovedownFixed(cm, true);}); - - function testMovedownResize(cm, hScroll) { - cm.getWrapperElement().style.height = "auto"; - if (hScroll) cm.setValue(new Array(100).join("x")); - cm.refresh(); - for (var i = 0; i < 30; i++) { - cm.replaceSelection("x\n"); - var bottom = displayBottom(cm, hScroll); - var cursorBottom = cm.cursorCoords(null, "window").bottom; - is(cursorBottom <= bottom); - is(cursorBottom >= bottom - 5); - } - } - - testCM("movedown_resize", function(cm) {testMovedownResize(cm, false);}); - testCM("movedown_hscroll_resize", function(cm) {testMovedownResize(cm, true);}); - - function testMoveright(cm, wrap, scroll) { - cm.setSize("100px", "100px"); - if (wrap) cm.setOption("lineWrapping", true); - if (scroll) { - cm.setValue("\n" + new Array(100).join("x\n")); - cm.setCursor(Pos(0, 0)); - } - var right = displayRight(cm, scroll); - for (var i = 0; i < 10; i++) { - cm.replaceSelection("xxxxxxxxxx"); - var cursorRight = cm.cursorCoords(null, "window").right; - is(cursorRight < right); - } - if (!wrap) is(cursorRight > right - 20); - } - - testCM("moveright", function(cm) {testMoveright(cm, false, false);}); - testCM("moveright_wrap", function(cm) {testMoveright(cm, true, false);}); - testCM("moveright_scroll", function(cm) {testMoveright(cm, false, true);}); - testCM("moveright_scroll_wrap", function(cm) {testMoveright(cm, true, true);}); - - testCM("suddenly_wide", function(cm) { - addDoc(cm, 100, 100); - cm.replaceSelection(new Array(600).join("l ") + "\n"); - cm.execCommand("goLineUp"); - cm.execCommand("goLineEnd"); - is(barH(cm).scrollLeft > cm.getScrollerElement().scrollLeft - 1); - }); - - testCM("wrap_changes_height", function(cm) { - var line = new Array(20).join("a ") + "\n"; - cm.setValue(new Array(20).join(line)); - var box = cm.getWrapperElement().getBoundingClientRect(); - cm.setSize(cm.cursorCoords(Pos(0), "window").right - box.left + 2, - cm.cursorCoords(Pos(19, 0), "window").bottom - box.top + 2); - cm.setCursor(Pos(19, 0)); - cm.replaceSelection("\n"); - is(cm.cursorCoords(null, "window").bottom < displayBottom(cm, false)); - }, {lineWrapping: true}); - - testCM("height_auto_with_gutter_expect_no_scroll_after_line_delete", function(cm) { - cm.setSize(null, "auto"); - cm.setValue("x\n"); - cm.execCommand("goDocEnd"); - cm.execCommand("delCharBefore"); - eq(cm.getScrollInfo().top, 0); - cm.scrollTo(null, 10); - is(cm.getScrollInfo().top < 5); - }, {lineNumbers: true}); - - testCM("bidi_ensureCursorVisible", function(cm) { - cm.setValue("
وضع الاستخدام. عندما لا تعطى، وهذا الافتراضي إلى الطريقة الاولى\n"); - cm.execCommand("goLineStart"); - eq(cm.getScrollInfo().left, 0); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 3, "before")); - eq(cm.getScrollInfo().left, 0); - }, {lineWrapping: false}); -})(); diff --git a/CODE/js/codemirror/test/search_test.js b/CODE/js/codemirror/test/search_test.js deleted file mode 100644 index 0e468c04..00000000 --- a/CODE/js/codemirror/test/search_test.js +++ /dev/null @@ -1,91 +0,0 @@ -(function() { - "use strict"; - - function run(doc, query, options) { - var cursor = doc.getSearchCursor(query, null, options); - for (var i = 3; i < arguments.length; i += 4) { - var found = cursor.findNext(); - is(found, "not enough results (forward)"); - eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, forward, " + (i - 3) / 4); - eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, forward, " + (i - 3) / 4); - } - is(!cursor.findNext(), "too many matches (forward)"); - for (var i = arguments.length - 4; i >= 3; i -= 4) { - var found = cursor.findPrevious(); - is(found, "not enough results (backwards)"); - eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, backwards, " + (i - 3) / 4); - eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, backwards, " + (i - 3) / 4); - } - is(!cursor.findPrevious(), "too many matches (backwards)"); - } - - function test(name, f) { window.test("search_" + name, f) } - - test("simple", function() { - var doc = new CodeMirror.Doc("abcdefg\nabcdefg") - run(doc, "cde", false, 0, 2, 0, 5, 1, 2, 1, 5); - }); - - test("multiline", function() { - var doc = new CodeMirror.Doc("hallo\na\nb\ngoodbye") - run(doc, "llo\na\nb\ngoo", false, 0, 2, 3, 3); - run(doc, "blah\na\nb\nhall", false); - run(doc, "bye\nx\neye", false); - }); - - test("regexp", function() { - var doc = new CodeMirror.Doc("abcde\nabcde") - run(doc, /bcd/, false, 0, 1, 0, 4, 1, 1, 1, 4); - run(doc, /BCD/, false); - run(doc, /BCD/i, false, 0, 1, 0, 4, 1, 1, 1, 4); - }); - - test("regexpMultiline", function() { - var doc = new CodeMirror.Doc("fom fom\nbar\nbaz") - run(doc, /fo[^]*az/, {multiline: true}, 0, 0, 2, 3) - run(doc, /[oa][^u]/, {multiline: true}, 0, 1, 0, 3, 0, 5, 0, 7, 1, 1, 1, 3, 2, 1, 2, 3) - run(doc, /[a][^u]{2}/, {multiline: true}, 1, 1, 2, 0) - }) - - test("insensitive", function() { - var doc = new CodeMirror.Doc("hallo\nHALLO\noink\nhAllO") - run(doc, "All", false, 3, 1, 3, 4); - run(doc, "All", true, 0, 1, 0, 4, 1, 1, 1, 4, 3, 1, 3, 4); - }); - - test("multilineInsensitive", function() { - var doc = new CodeMirror.Doc("zie ginds komT\nDe Stoomboot\nuit Spanje weer aan") - run(doc, "komt\nde stoomboot\nuit", false); - run(doc, "komt\nde stoomboot\nuit", {caseFold: true}, 0, 10, 2, 3); - run(doc, "kOMt\ndE stOOmboot\nuiT", {caseFold: true}, 0, 10, 2, 3); - }); - - test("multilineInsensitiveSlow", function() { - var text = "" - for (var i = 0; i < 1000; i++) text += "foo\nbar\n" - var doc = new CodeMirror.Doc("find\nme\n" + text + "find\nme\n") - var t0 = +new Date - run(doc, /find\nme/, {multiline: true}, 0, 0, 1, 2, 2002, 0, 2003, 2) - is(+new Date - t0 < 100) - }) - - test("expandingCaseFold", function() { - var doc = new CodeMirror.Doc("İİ İİ\nuu uu") - run(doc, "", true, 0, 8, 0, 12, 1, 8, 1, 12); - run(doc, "İİ", true, 0, 3, 0, 5, 0, 6, 0, 8); - }); - - test("normalize", function() { - if (!String.prototype.normalize) return - var doc = new CodeMirror.Doc("yılbaşı\n수 있을까\nLe taux d'humidité à London") - run(doc, "s", false, 0, 5, 0, 6) - run(doc, "이", false, 1, 2, 1, 3) - run(doc, "a", false, 0, 4, 0, 5, 2, 4, 2, 5, 2, 19, 2, 20) - }) - - test("endOfLine", function() { - var doc = new CodeMirror.Doc("bbcdb\nabcd\nbbcdb\nabcd") - run(doc, /[^b]$/, {multiline: true}, 1, 3, 1, 4, 3, 3, 3, 4) - run(doc, /b$/, false, 0, 4, 0, 5, 2, 4, 2, 5) - }) -})(); diff --git a/CODE/js/codemirror/test/sql-hint-test.js b/CODE/js/codemirror/test/sql-hint-test.js deleted file mode 100644 index d1ea7f1f..00000000 --- a/CODE/js/codemirror/test/sql-hint-test.js +++ /dev/null @@ -1,301 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: https://codemirror.net/LICENSE - -(function() { - var Pos = CodeMirror.Pos; - - var simpleTables = { - "users": ["name", "score", "birthDate"], - "xcountries": ["name", "population", "size"] - }; - - var schemaTables = { - "schema.users": ["name", "score", "birthDate"], - "schema.countries": ["name", "population", "size"] - }; - - var displayTextTables = [{ - text: "mytable", - displayText: "mytable | The main table", - columns: [{text: "id", displayText: "id | Unique ID"}, - {text: "name", displayText: "name | The name"}] - }]; - - var displayTextTablesWithDefault = [ - { - text: "Api__TokenAliases", - columns: [ - { - text: "token", - displayText: "token | varchar(255) | Primary", - columnName: "token", - columnHint: "varchar(255) | Primary" - }, - { - text: "alias", - displayText: "alias | varchar(255) | Primary", - columnName: "alias", - columnHint: "varchar(255) | Primary" - } - ] - }, - { - text: "mytable", - columns: [ - { text: "id", displayText: "id | Unique ID" }, - { text: "name", displayText: "name | The name" } - ] - } - ]; - - namespace = "sql-hint_"; - - function test(name, spec) { - testCM(name, function(cm) { - cm.setValue(spec.value); - cm.setCursor(spec.cursor); - var completion = CodeMirror.hint.sql(cm, { - tables: spec.tables, - defaultTable: spec.defaultTable, - disableKeywords: spec.disableKeywords - }); - if (!deepCompare(completion.list, spec.list)) - throw new Failure("Wrong completion results " + JSON.stringify(completion.list) + " vs " + JSON.stringify(spec.list)); - eqCharPos(completion.from, spec.from); - eqCharPos(completion.to, spec.to); - }, { - value: spec.value, - mode: spec.mode || "text/x-mysql" - }); - } - - test("keywords", { - value: "SEL", - cursor: Pos(0, 3), - list: [{"text":"SELECT","className":"CodeMirror-hint-keyword"}], - from: Pos(0, 0), - to: Pos(0, 3) - }); - - test("keywords_disabled", { - value: "SEL", - cursor: Pos(0, 3), - disableKeywords: true, - list: [], - from: Pos(0, 0), - to: Pos(0, 3) - }); - - test("from", { - value: "SELECT * fr", - cursor: Pos(0, 11), - list: [{"text":"FROM","className":"CodeMirror-hint-keyword"}], - from: Pos(0, 9), - to: Pos(0, 11) - }); - - test("table", { - value: "SELECT xc", - cursor: Pos(0, 9), - tables: simpleTables, - list: [{"text":"xcountries","className":"CodeMirror-hint-table"}], - from: Pos(0, 7), - to: Pos(0, 9) - }); - - test("columns", { - value: "SELECT users.", - cursor: Pos(0, 13), - tables: simpleTables, - list: ["users.name", "users.score", "users.birthDate"], - from: Pos(0, 7), - to: Pos(0, 13) - }); - - test("singlecolumn", { - value: "SELECT users.na", - cursor: Pos(0, 15), - tables: simpleTables, - list: ["users.name"], - from: Pos(0, 7), - to: Pos(0, 15) - }); - - test("quoted", { - value: "SELECT `users`.`na", - cursor: Pos(0, 18), - tables: simpleTables, - list: ["`users`.`name`"], - from: Pos(0, 7), - to: Pos(0, 18) - }); - - test("doublequoted", { - value: "SELECT \"users\".\"na", - cursor: Pos(0, 18), - tables: simpleTables, - list: ["\"users\".\"name\""], - from: Pos(0, 7), - to: Pos(0, 18), - mode: "text/x-sqlite" - }); - - test("quotedcolumn", { - value: "SELECT users.`na", - cursor: Pos(0, 16), - tables: simpleTables, - list: ["`users`.`name`"], - from: Pos(0, 7), - to: Pos(0, 16) - }); - - test("doublequotedcolumn", { - value: "SELECT users.\"na", - cursor: Pos(0, 16), - tables: simpleTables, - list: ["\"users\".\"name\""], - from: Pos(0, 7), - to: Pos(0, 16), - mode: "text/x-sqlite" - }); - - test("schema", { - value: "SELECT schem", - cursor: Pos(0, 12), - tables: schemaTables, - list: [{"text":"schema.users","className":"CodeMirror-hint-table"}, - {"text":"schema.countries","className":"CodeMirror-hint-table"}, - {"text":"SCHEMA","className":"CodeMirror-hint-keyword"}, - {"text":"SCHEMA_NAME","className":"CodeMirror-hint-keyword"}, - {"text":"SCHEMAS","className":"CodeMirror-hint-keyword"}], - from: Pos(0, 7), - to: Pos(0, 12) - }); - - test("schemaquoted", { - value: "SELECT `sch", - cursor: Pos(0, 11), - tables: schemaTables, - list: ["`schema`.`users`", "`schema`.`countries`"], - from: Pos(0, 7), - to: Pos(0, 11) - }); - - test("schemadoublequoted", { - value: "SELECT \"sch", - cursor: Pos(0, 11), - tables: schemaTables, - list: ["\"schema\".\"users\"", "\"schema\".\"countries\""], - from: Pos(0, 7), - to: Pos(0, 11), - mode: "text/x-sqlite" - }); - - test("schemacolumn", { - value: "SELECT schema.users.", - cursor: Pos(0, 20), - tables: schemaTables, - list: ["schema.users.name", - "schema.users.score", - "schema.users.birthDate"], - from: Pos(0, 7), - to: Pos(0, 20) - }); - - test("schemacolumnquoted", { - value: "SELECT `schema`.`users`.", - cursor: Pos(0, 24), - tables: schemaTables, - list: ["`schema`.`users`.`name`", - "`schema`.`users`.`score`", - "`schema`.`users`.`birthDate`"], - from: Pos(0, 7), - to: Pos(0, 24) - }); - - test("schemacolumndoublequoted", { - value: "SELECT \"schema\".\"users\".", - cursor: Pos(0, 24), - tables: schemaTables, - list: ["\"schema\".\"users\".\"name\"", - "\"schema\".\"users\".\"score\"", - "\"schema\".\"users\".\"birthDate\""], - from: Pos(0, 7), - to: Pos(0, 24), - mode: "text/x-sqlite" - }); - - test("displayText_default_table", { - value: "SELECT a", - cursor: Pos(0, 8), - disableKeywords: true, - defaultTable: "Api__TokenAliases", - tables: displayTextTablesWithDefault, - list: [ - { - text: "alias", - displayText: "alias | varchar(255) | Primary", - columnName: "alias", - columnHint: "varchar(255) | Primary", - className: "CodeMirror-hint-table CodeMirror-hint-default-table" - }, - { text: "Api__TokenAliases", className: "CodeMirror-hint-table" } - ], - from: Pos(0, 7), - to: Pos(0, 8) - }); - - test("displayText_table", { - value: "SELECT myt", - cursor: Pos(0, 10), - tables: displayTextTables, - list: [{text: "mytable", displayText: "mytable | The main table", "className":"CodeMirror-hint-table"}], - from: Pos(0, 7), - to: Pos(0, 10) - }); - - test("displayText_column", { - value: "SELECT mytable.", - cursor: Pos(0, 15), - tables: displayTextTables, - list: [{text: "mytable.id", displayText: "id | Unique ID"}, - {text: "mytable.name", displayText: "name | The name"}], - from: Pos(0, 7), - to: Pos(0, 15) - }); - - test("alias_complete", { - value: "SELECT t. FROM users t", - cursor: Pos(0, 9), - tables: simpleTables, - list: ["t.name", "t.score", "t.birthDate"], - from: Pos(0, 7), - to: Pos(0, 9) - }); - - test("alias_complete_with_displayText", { - value: "SELECT t. FROM mytable t", - cursor: Pos(0, 9), - tables: displayTextTables, - list: [{text: "t.id", displayText: "id | Unique ID"}, - {text: "t.name", displayText: "name | The name"}], - from: Pos(0, 7), - to: Pos(0, 9) - }) - - function deepCompare(a, b) { - if (a === b) return true - if (!(a && typeof a == "object") || - !(b && typeof b == "object")) return false - var array = Array.isArray(a) - if (Array.isArray(b) != array) return false - if (array) { - if (a.length != b.length) return false - for (var i = 0; i < a.length; i++) if (!deepCompare(a[i], b[i])) return false - } else { - for (var p in a) if (!(p in b) || !deepCompare(a[p], b[p])) return false - for (var p in b) if (!(p in a)) return false - } - return true - } -})(); diff --git a/CODE/js/codemirror/test/sublime_test.js b/CODE/js/codemirror/test/sublime_test.js deleted file mode 100644 index 57fd385e..00000000 --- a/CODE/js/codemirror/test/sublime_test.js +++ /dev/null @@ -1,295 +0,0 @@ -(function() { - "use strict"; - - var Pos = CodeMirror.Pos; - namespace = "sublime_"; - - function stTest(name) { - var actions = Array.prototype.slice.call(arguments, 1); - testCM(name, function(cm) { - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (typeof action == "string" && i == 0) - cm.setValue(action); - else if (typeof action == "string") - cm.execCommand(action); - else if (action instanceof Pos) - cm.setCursor(action); - else - action(cm); - } - }); - } - - function at(line, ch, msg) { - return function(cm) { - eq(cm.listSelections().length, 1); - eqCursorPos(cm.getCursor("head"), Pos(line, ch), msg); - eqCursorPos(cm.getCursor("anchor"), Pos(line, ch), msg); - }; - } - - function val(content, msg) { - return function(cm) { eq(cm.getValue(), content, msg); }; - } - - function argsToRanges(args) { - if (args.length % 4) throw new Error("Wrong number of arguments for ranges."); - var ranges = []; - for (var i = 0; i < args.length; i += 4) - ranges.push({anchor: Pos(args[i], args[i + 1]), - head: Pos(args[i + 2], args[i + 3])}); - return ranges; - } - - function setSel() { - var ranges = argsToRanges(arguments); - return function(cm) { cm.setSelections(ranges, 0); }; - } - - function hasSel() { - var ranges = argsToRanges(arguments); - return function(cm) { - var sels = cm.listSelections(); - if (sels.length != ranges.length) - throw new Failure("Expected " + ranges.length + " selections, but found " + sels.length); - for (var i = 0; i < sels.length; i++) { - eqCharPos(sels[i].anchor, ranges[i].anchor, "anchor " + i); - eqCharPos(sels[i].head, ranges[i].head, "head " + i); - } - }; - } - - stTest("bySubword", "the foo_bar DooDahBah \n a FOOBar", - "goSubwordLeft", at(0, 0), - "goSubwordRight", at(0, 3), - "goSubwordRight", at(0, 7), - "goSubwordRight", at(0, 11), - "goSubwordRight", at(0, 15), - "goSubwordRight", at(0, 18), - "goSubwordRight", at(0, 21), - "goSubwordRight", at(0, 22), - "goSubwordRight", at(1, 0), - "goSubwordRight", at(1, 2), - "goSubwordRight", at(1, 6), - "goSubwordRight", at(1, 9), - "goSubwordLeft", at(1, 6), - "goSubwordLeft", at(1, 3), - "goSubwordLeft", at(1, 1), - "goSubwordLeft", at(1, 0), - "goSubwordLeft", at(0, 22), - "goSubwordLeft", at(0, 18), - "goSubwordLeft", at(0, 15), - "goSubwordLeft", at(0, 12), - "goSubwordLeft", at(0, 8), - "goSubwordLeft", at(0, 4), - "goSubwordLeft", at(0, 0)); - - stTest("splitSelectionByLine", "abc\ndef\nghi", - setSel(0, 1, 2, 2), - "splitSelectionByLine", - hasSel(0, 1, 0, 3, - 1, 0, 1, 3, - 2, 0, 2, 2)); - - stTest("splitSelectionByLineMulti", "abc\ndef\nghi\njkl", - setSel(0, 1, 1, 1, - 1, 2, 3, 2, - 3, 3, 3, 3), - "splitSelectionByLine", - hasSel(0, 1, 0, 3, - 1, 0, 1, 1, - 1, 2, 1, 3, - 2, 0, 2, 3, - 3, 0, 3, 2, - 3, 3, 3, 3)); - - stTest("selectLine", "abc\ndef\nghi", - setSel(0, 1, 0, 1, - 2, 0, 2, 1), - "selectLine", - hasSel(0, 0, 1, 0, - 2, 0, 2, 3), - setSel(0, 1, 1, 0), - "selectLine", - hasSel(0, 0, 2, 0)); - - stTest("insertLineAfter", "abcde\nfghijkl\nmn", - setSel(0, 1, 0, 1, - 0, 3, 0, 3, - 1, 2, 1, 2, - 1, 3, 1, 5), "insertLineAfter", - hasSel(1, 0, 1, 0, - 3, 0, 3, 0), val("abcde\n\nfghijkl\n\nmn")); - - stTest("insertLineBefore", "abcde\nfghijkl\nmn", - setSel(0, 1, 0, 1, - 0, 3, 0, 3, - 1, 2, 1, 2, - 1, 3, 1, 5), "insertLineBefore", - hasSel(0, 0, 0, 0, - 2, 0, 2, 0), val("\nabcde\n\nfghijkl\nmn")); - - stTest("skipAndSelectNextOccurrence", "a foo bar\nfoobar foo", - setSel(0, 2, 0, 5), "skipAndSelectNextOccurrence", hasSel(1, 0, 1, 3), - "skipAndSelectNextOccurrence", hasSel(1, 7, 1, 10), - "skipAndSelectNextOccurrence", hasSel(0, 2, 0, 5), - Pos(0, 3), "skipAndSelectNextOccurrence", hasSel(0, 2, 0, 5), - "skipAndSelectNextOccurrence", hasSel(1, 7, 1, 10), - setSel(0, 6, 0, 9), "skipAndSelectNextOccurrence", hasSel(1, 3, 1, 6)); - - stTest("selectNextOccurrence", "a foo bar\nfoobar foo", - setSel(0, 2, 0, 5), - "selectNextOccurrence", hasSel(0, 2, 0, 5, - 1, 0, 1, 3), - "selectNextOccurrence", hasSel(0, 2, 0, 5, - 1, 0, 1, 3, - 1, 7, 1, 10), - "selectNextOccurrence", hasSel(0, 2, 0, 5, - 1, 0, 1, 3, - 1, 7, 1, 10), - Pos(0, 3), "selectNextOccurrence", hasSel(0, 2, 0, 5), - "selectNextOccurrence", hasSel(0, 2, 0, 5, - 1, 7, 1, 10), - setSel(0, 6, 0, 9), - "selectNextOccurrence", hasSel(0, 6, 0, 9, - 1, 3, 1, 6)); - - stTest("selectScope", "foo(a) {\n bar[1, 2];\n}", - "selectScope", hasSel(0, 0, 2, 1), - Pos(0, 4), "selectScope", hasSel(0, 4, 0, 5), - Pos(0, 5), "selectScope", hasSel(0, 4, 0, 5), - Pos(0, 6), "selectScope", hasSel(0, 0, 2, 1), - Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0), - Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0), - Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10), - Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10), - "selectScope", hasSel(0, 8, 2, 0), - "selectScope", hasSel(0, 0, 2, 1)); - - stTest("goToBracket", "foo(a) {\n bar[1, 2];\n}", - Pos(0, 0), "goToBracket", at(0, 0), - Pos(0, 4), "goToBracket", at(0, 5), "goToBracket", at(0, 4), - Pos(0, 8), "goToBracket", at(2, 0), "goToBracket", at(0, 8), - Pos(1, 2), "goToBracket", at(2, 0), - Pos(1, 7), "goToBracket", at(1, 10), "goToBracket", at(1, 6)); - - stTest("swapLine", "1\n2\n3---\n4\n5", - "swapLineDown", val("2\n1\n3---\n4\n5"), - "swapLineUp", val("1\n2\n3---\n4\n5"), - "swapLineUp", val("1\n2\n3---\n4\n5"), - Pos(4, 1), "swapLineDown", val("1\n2\n3---\n4\n5"), - setSel(0, 1, 0, 1, - 1, 0, 2, 0, - 2, 2, 2, 2), - "swapLineDown", val("4\n1\n2\n3---\n5"), - hasSel(1, 1, 1, 1, - 2, 0, 3, 0, - 3, 2, 3, 2), - "swapLineUp", val("1\n2\n3---\n4\n5"), - hasSel(0, 1, 0, 1, - 1, 0, 2, 0, - 2, 2, 2, 2)); - - stTest("swapLineEmptyBottomSel", "1\n2\n3", - setSel(0, 1, 1, 0), - "swapLineDown", val("2\n1\n3"), hasSel(1, 1, 2, 0), - "swapLineUp", val("1\n2\n3"), hasSel(0, 1, 1, 0), - "swapLineUp", val("1\n2\n3"), hasSel(0, 0, 0, 0)); - - stTest("swapLineUpFromEnd", "a\nb\nc", - Pos(2, 1), "swapLineUp", - hasSel(1, 1, 1, 1), val("a\nc\nb")); - - stTest("joinLines", "abc\ndef\nghi\njkl", - "joinLines", val("abc def\nghi\njkl"), at(0, 4), - "undo", - setSel(0, 2, 1, 1), "joinLines", - val("abc def ghi\njkl"), hasSel(0, 2, 0, 8), - "undo", - setSel(0, 1, 0, 1, - 1, 1, 1, 1, - 3, 1, 3, 1), "joinLines", - val("abc def ghi\njkl"), hasSel(0, 4, 0, 4, - 0, 8, 0, 8, - 1, 3, 1, 3)); - - stTest("duplicateLine", "abc\ndef\nghi", - Pos(1, 0), "duplicateLine", val("abc\ndef\ndef\nghi"), at(2, 0), - "undo", - setSel(0, 1, 0, 1, - 1, 1, 1, 1, - 2, 1, 2, 1), "duplicateLine", - val("abc\nabc\ndef\ndef\nghi\nghi"), hasSel(1, 1, 1, 1, - 3, 1, 3, 1, - 5, 1, 5, 1)); - stTest("duplicateLineSelection", "abcdef", - setSel(0, 1, 0, 1, - 0, 2, 0, 4, - 0, 5, 0, 5), - "duplicateLine", - val("abcdef\nabcdcdef\nabcdcdef"), hasSel(2, 1, 2, 1, - 2, 4, 2, 6, - 2, 7, 2, 7)); - - stTest("sortLines", "c\nb\na\nC\nB\nA", - "sortLines", val("A\nB\nC\na\nb\nc"), - "undo", - setSel(0, 0, 2, 0, - 3, 0, 5, 0), - "sortLines", val("b\nc\na\nB\nC\nA"), - hasSel(0, 0, 2, 0, - 3, 0, 5, 0), - "undo", - setSel(1, 0, 5, 0), "sortLinesInsensitive", val("c\na\nB\nb\nC\nA")); - - stTest("bookmarks", "abc\ndef\nghi\njkl", - Pos(0, 1), "toggleBookmark", - setSel(1, 1, 1, 2), "toggleBookmark", - setSel(2, 1, 2, 2), "toggleBookmark", - "nextBookmark", hasSel(0, 1, 0, 1), - "nextBookmark", hasSel(1, 1, 1, 2), - "nextBookmark", hasSel(2, 1, 2, 2), - "prevBookmark", hasSel(1, 1, 1, 2), - "prevBookmark", hasSel(0, 1, 0, 1), - "prevBookmark", hasSel(2, 1, 2, 2), - "prevBookmark", hasSel(1, 1, 1, 2), - "toggleBookmark", - "prevBookmark", hasSel(2, 1, 2, 2), - "prevBookmark", hasSel(0, 1, 0, 1), - "selectBookmarks", hasSel(0, 1, 0, 1, - 2, 1, 2, 2), - "clearBookmarks", - Pos(0, 0), "selectBookmarks", at(0, 0)); - - stTest("smartBackspace", " foo\n bar", - setSel(0, 2, 0, 2, 1, 4, 1, 4, 1, 6, 1, 6), "smartBackspace", - val("foo\n br")) - - stTest("upAndDowncaseAtCursor", "abc\ndef x\nghI", - setSel(0, 1, 0, 3, - 1, 1, 1, 1, - 1, 4, 1, 4), "upcaseAtCursor", - val("aBC\nDEF x\nghI"), hasSel(0, 1, 0, 3, - 1, 3, 1, 3, - 1, 4, 1, 4), - "downcaseAtCursor", - val("abc\ndef x\nghI"), hasSel(0, 1, 0, 3, - 1, 3, 1, 3, - 1, 4, 1, 4)); - - stTest("mark", "abc\ndef\nghi", - Pos(1, 1), "setSublimeMark", - Pos(2, 1), "selectToSublimeMark", hasSel(2, 1, 1, 1), - Pos(0, 1), "swapWithSublimeMark", at(1, 1), "swapWithSublimeMark", at(0, 1), - "deleteToSublimeMark", val("aef\nghi"), - "sublimeYank", val("abc\ndef\nghi"), at(1, 1)); - - stTest("findUnder", "foo foobar a", - "findUnder", hasSel(0, 4, 0, 7), - "findUnder", hasSel(0, 0, 0, 3), - "findUnderPrevious", hasSel(0, 4, 0, 7), - "findUnderPrevious", hasSel(0, 0, 0, 3), - Pos(0, 4), "findUnder", hasSel(0, 4, 0, 10), - Pos(0, 11), "findUnder", hasSel(0, 11, 0, 11)); -})(); diff --git a/CODE/js/codemirror/test/test.js b/CODE/js/codemirror/test/test.js deleted file mode 100644 index 2a5101f4..00000000 --- a/CODE/js/codemirror/test/test.js +++ /dev/null @@ -1,2674 +0,0 @@ -var Pos = CodeMirror.Pos; - -CodeMirror.defaults.rtlMoveVisually = true; - -function forEach(arr, f) { - for (var i = 0, e = arr.length; i < e; ++i) f(arr[i], i); -} - -function addDoc(cm, width, height) { - var content = [], line = ""; - for (var i = 0; i < width; ++i) line += "x"; - for (var i = 0; i < height; ++i) content.push(line); - cm.setValue(content.join("\n")); -} - -function byClassName(elt, cls) { - if (elt.getElementsByClassName) return elt.getElementsByClassName(cls); - var found = [], re = new RegExp("\\b" + cls + "\\b"); - function search(elt) { - if (elt.nodeType == 3) return; - if (re.test(elt.className)) found.push(elt); - for (var i = 0, e = elt.childNodes.length; i < e; ++i) - search(elt.childNodes[i]); - } - search(elt); - return found; -} - -var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); -var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); -var mac = /Mac/.test(navigator.platform); -var opera = /Opera\/\./.test(navigator.userAgent); -var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/); -if (opera_version) opera_version = Number(opera_version); -var opera_lt10 = opera && (!opera_version || opera_version < 10); - -namespace = "core_"; - -test("core_fromTextArea", function() { - var te = document.getElementById("code"); - te.value = "CONTENT"; - var cm = CodeMirror.fromTextArea(te); - is(!te.offsetHeight); - eq(cm.getValue(), "CONTENT"); - cm.setValue("foo\nbar"); - eq(cm.getValue(), "foo\nbar"); - cm.save(); - is(/^foo\r?\nbar$/.test(te.value)); - cm.setValue("xxx"); - cm.toTextArea(); - is(te.offsetHeight); - eq(te.value, "xxx"); -}); - -testCM("getRange", function(cm) { - eq(cm.getLine(0), "1234"); - eq(cm.getLine(1), "5678"); - eq(cm.getLine(2), null); - eq(cm.getLine(-1), null); - eq(cm.getRange(Pos(0, 0), Pos(0, 3)), "123"); - eq(cm.getRange(Pos(0, -1), Pos(0, 200)), "1234"); - eq(cm.getRange(Pos(0, 2), Pos(1, 2)), "34\n56"); - eq(cm.getRange(Pos(1, 2), Pos(100, 0)), "78"); -}, {value: "1234\n5678"}); - -testCM("replaceRange", function(cm) { - eq(cm.getValue(), ""); - cm.replaceRange("foo\n", Pos(0, 0)); - eq(cm.getValue(), "foo\n"); - cm.replaceRange("a\nb", Pos(0, 1)); - eq(cm.getValue(), "fa\nboo\n"); - eq(cm.lineCount(), 3); - cm.replaceRange("xyzzy", Pos(0, 0), Pos(1, 1)); - eq(cm.getValue(), "xyzzyoo\n"); - cm.replaceRange("abc", Pos(0, 0), Pos(10, 0)); - eq(cm.getValue(), "abc"); - eq(cm.lineCount(), 1); -}); - -testCM("selection", function(cm) { - cm.setSelection(Pos(0, 4), Pos(2, 2)); - is(cm.somethingSelected()); - eq(cm.getSelection(), "11\n222222\n33"); - eqCursorPos(cm.getCursor(false), Pos(2, 2)); - eqCursorPos(cm.getCursor(true), Pos(0, 4)); - cm.setSelection(Pos(1, 0)); - is(!cm.somethingSelected()); - eq(cm.getSelection(), ""); - eqCursorPos(cm.getCursor(true), Pos(1, 0)); - cm.replaceSelection("abc", "around"); - eq(cm.getSelection(), "abc"); - eq(cm.getValue(), "111111\nabc222222\n333333"); - cm.replaceSelection("def", "end"); - eq(cm.getSelection(), ""); - eqCursorPos(cm.getCursor(true), Pos(1, 3)); - cm.setCursor(Pos(2, 1)); - eqCursorPos(cm.getCursor(true), Pos(2, 1)); - cm.setCursor(1, 2); - eqCursorPos(cm.getCursor(true), Pos(1, 2)); -}, {value: "111111\n222222\n333333"}); - -testCM("extendSelection", function(cm) { - cm.setExtending(true); - addDoc(cm, 10, 10); - cm.setSelection(Pos(3, 5)); - eqCursorPos(cm.getCursor("head"), Pos(3, 5)); - eqCursorPos(cm.getCursor("anchor"), Pos(3, 5)); - cm.setSelection(Pos(2, 5), Pos(5, 5)); - eqCursorPos(cm.getCursor("head"), Pos(5, 5)); - eqCursorPos(cm.getCursor("anchor"), Pos(2, 5)); - eqCursorPos(cm.getCursor("start"), Pos(2, 5)); - eqCursorPos(cm.getCursor("end"), Pos(5, 5)); - cm.setSelection(Pos(5, 5), Pos(2, 5)); - eqCursorPos(cm.getCursor("head"), Pos(2, 5)); - eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); - eqCursorPos(cm.getCursor("start"), Pos(2, 5)); - eqCursorPos(cm.getCursor("end"), Pos(5, 5)); - cm.extendSelection(Pos(3, 2)); - eqCursorPos(cm.getCursor("head"), Pos(3, 2)); - eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); - cm.extendSelection(Pos(6, 2)); - eqCursorPos(cm.getCursor("head"), Pos(6, 2)); - eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); - cm.extendSelection(Pos(6, 3), Pos(6, 4)); - eqCursorPos(cm.getCursor("head"), Pos(6, 4)); - eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); - cm.extendSelection(Pos(0, 3), Pos(0, 4)); - eqCursorPos(cm.getCursor("head"), Pos(0, 3)); - eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); - cm.extendSelection(Pos(4, 5), Pos(6, 5)); - eqCursorPos(cm.getCursor("head"), Pos(6, 5)); - eqCursorPos(cm.getCursor("anchor"), Pos(4, 5)); - cm.setExtending(false); - cm.extendSelection(Pos(0, 3), Pos(0, 4)); - eqCursorPos(cm.getCursor("head"), Pos(0, 3)); - eqCursorPos(cm.getCursor("anchor"), Pos(0, 4)); -}); - -testCM("lines", function(cm) { - eq(cm.getLine(0), "111111"); - eq(cm.getLine(1), "222222"); - eq(cm.getLine(-1), null); - cm.replaceRange("", Pos(1, 0), Pos(2, 0)) - cm.replaceRange("abc", Pos(1, 0), Pos(1)); - eq(cm.getValue(), "111111\nabc"); -}, {value: "111111\n222222\n333333"}); - -testCM("indent", function(cm) { - cm.indentLine(1); - eq(cm.getLine(1), " blah();"); - cm.setOption("indentUnit", 8); - cm.indentLine(1); - eq(cm.getLine(1), "\tblah();"); - cm.setOption("indentUnit", 10); - cm.setOption("tabSize", 4); - cm.indentLine(1); - eq(cm.getLine(1), "\t\t blah();"); -}, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8}); - -testCM("indentByNumber", function(cm) { - cm.indentLine(0, 2); - eq(cm.getLine(0), " foo"); - cm.indentLine(0, -200); - eq(cm.getLine(0), "foo"); - cm.setSelection(Pos(0, 0), Pos(1, 2)); - cm.indentSelection(3); - eq(cm.getValue(), " foo\n bar\nbaz"); -}, {value: "foo\nbar\nbaz"}); - -test("core_defaults", function() { - var defsCopy = {}, defs = CodeMirror.defaults; - for (var opt in defs) defsCopy[opt] = defs[opt]; - defs.indentUnit = 5; - defs.value = "uu"; - defs.indentWithTabs = true; - defs.tabindex = 55; - var place = document.getElementById("testground"), cm = CodeMirror(place); - try { - eq(cm.getOption("indentUnit"), 5); - cm.setOption("indentUnit", 10); - eq(defs.indentUnit, 5); - eq(cm.getValue(), "uu"); - eq(cm.getOption("indentWithTabs"), true); - eq(cm.getInputField().tabIndex, 55); - } - finally { - for (var opt in defsCopy) defs[opt] = defsCopy[opt]; - place.removeChild(cm.getWrapperElement()); - } -}); - -testCM("lineInfo", function(cm) { - eq(cm.lineInfo(-1), null); - var mark = document.createElement("span"); - var lh = cm.setGutterMarker(1, "FOO", mark); - var info = cm.lineInfo(1); - eq(info.text, "222222"); - eq(info.gutterMarkers.FOO, mark); - eq(info.line, 1); - eq(cm.lineInfo(2).gutterMarkers, null); - cm.setGutterMarker(lh, "FOO", null); - eq(cm.lineInfo(1).gutterMarkers, null); - cm.setGutterMarker(1, "FOO", mark); - cm.setGutterMarker(0, "FOO", mark); - cm.clearGutter("FOO"); - eq(cm.lineInfo(0).gutterMarkers, null); - eq(cm.lineInfo(1).gutterMarkers, null); -}, {value: "111111\n222222\n333333"}); - -testCM("coords", function(cm) { - cm.setSize(null, 100); - addDoc(cm, 32, 200); - var top = cm.charCoords(Pos(0, 0)); - var bot = cm.charCoords(Pos(200, 30)); - is(top.left < bot.left); - is(top.top < bot.top); - is(top.top < top.bottom); - cm.scrollTo(null, 100); - var top2 = cm.charCoords(Pos(0, 0)); - is(top.top > top2.top); - eq(top.left, top2.left); -}); - -testCM("coordsChar", function(cm) { - addDoc(cm, 35, 70); - for (var i = 0; i < 2; ++i) { - var sys = i ? "local" : "page"; - for (var ch = 0; ch <= 35; ch += 5) { - for (var line = 0; line < 70; line += 5) { - cm.setCursor(line, ch); - var coords = cm.charCoords(Pos(line, ch), sys); - var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys); - eqCharPos(pos, Pos(line, ch)); - } - } - } -}, {lineNumbers: true}); - -testCM("coordsCharBidi", function(cm) { - addDoc(cm, 35, 70); - // Put an rtl character into each line to trigger the bidi code path in coordsChar - cm.setValue(cm.getValue().replace(/\bx/g, 'و')) - for (var i = 0; i < 2; ++i) { - var sys = i ? "local" : "page"; - for (var ch = 2; ch <= 35; ch += 5) { - for (var line = 0; line < 70; line += 5) { - cm.setCursor(line, ch); - var coords = cm.charCoords(Pos(line, ch), sys); - var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys); - eqCharPos(pos, Pos(line, ch)); - } - } - } -}, {lineNumbers: true}); - -testCM("badBidiOptimization", function(cm) { - if (window.automatedTests) return - var coords = cm.charCoords(Pos(0, 34)) - eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34)) -}, {value: "----------

هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟

"}) - -testCM("posFromIndex", function(cm) { - cm.setValue( - "This function should\n" + - "convert a zero based index\n" + - "to line and ch." - ); - - var examples = [ - { index: -1, line: 0, ch: 0 }, // <- Tests clipping - { index: 0, line: 0, ch: 0 }, - { index: 10, line: 0, ch: 10 }, - { index: 39, line: 1, ch: 18 }, - { index: 55, line: 2, ch: 7 }, - { index: 63, line: 2, ch: 15 }, - { index: 64, line: 2, ch: 15 } // <- Tests clipping - ]; - - for (var i = 0; i < examples.length; i++) { - var example = examples[i]; - var pos = cm.posFromIndex(example.index); - eq(pos.line, example.line); - eq(pos.ch, example.ch); - if (example.index >= 0 && example.index < 64) - eq(cm.indexFromPos(pos), example.index); - } -}); - -testCM("undo", function(cm) { - cm.replaceRange("def", Pos(0, 0), Pos(0)); - eq(cm.historySize().undo, 1); - cm.undo(); - eq(cm.getValue(), "abc"); - eq(cm.historySize().undo, 0); - eq(cm.historySize().redo, 1); - cm.redo(); - eq(cm.getValue(), "def"); - eq(cm.historySize().undo, 1); - eq(cm.historySize().redo, 0); - cm.setValue("1\n\n\n2"); - cm.clearHistory(); - eq(cm.historySize().undo, 0); - for (var i = 0; i < 20; ++i) { - cm.replaceRange("a", Pos(0, 0)); - cm.replaceRange("b", Pos(3, 0)); - } - eq(cm.historySize().undo, 40); - for (var i = 0; i < 40; ++i) - cm.undo(); - eq(cm.historySize().redo, 40); - eq(cm.getValue(), "1\n\n\n2"); -}, {value: "abc"}); - -testCM("undoDepth", function(cm) { - cm.replaceRange("d", Pos(0)); - cm.replaceRange("e", Pos(0)); - cm.replaceRange("f", Pos(0)); - cm.undo(); cm.undo(); cm.undo(); - eq(cm.getValue(), "abcd"); -}, {value: "abc", undoDepth: 4}); - -testCM("undoDoesntClearValue", function(cm) { - cm.undo(); - eq(cm.getValue(), "x"); -}, {value: "x"}); - -testCM("undoMultiLine", function(cm) { - cm.operation(function() { - cm.replaceRange("x", Pos(0, 0)); - cm.replaceRange("y", Pos(1, 0)); - }); - cm.undo(); - eq(cm.getValue(), "abc\ndef\nghi"); - cm.operation(function() { - cm.replaceRange("y", Pos(1, 0)); - cm.replaceRange("x", Pos(0, 0)); - }); - cm.undo(); - eq(cm.getValue(), "abc\ndef\nghi"); - cm.operation(function() { - cm.replaceRange("y", Pos(2, 0)); - cm.replaceRange("x", Pos(1, 0)); - cm.replaceRange("z", Pos(2, 0)); - }); - cm.undo(); - eq(cm.getValue(), "abc\ndef\nghi", 3); -}, {value: "abc\ndef\nghi"}); - -testCM("undoComposite", function(cm) { - cm.replaceRange("y", Pos(1)); - cm.operation(function() { - cm.replaceRange("x", Pos(0)); - cm.replaceRange("z", Pos(2)); - }); - eq(cm.getValue(), "ax\nby\ncz\n"); - cm.undo(); - eq(cm.getValue(), "a\nby\nc\n"); - cm.undo(); - eq(cm.getValue(), "a\nb\nc\n"); - cm.redo(); cm.redo(); - eq(cm.getValue(), "ax\nby\ncz\n"); -}, {value: "a\nb\nc\n"}); - -testCM("undoSelection", function(cm) { - cm.setSelection(Pos(0, 2), Pos(0, 4)); - cm.replaceSelection(""); - cm.setCursor(Pos(1, 0)); - cm.undo(); - eqCursorPos(cm.getCursor(true), Pos(0, 2)); - eqCursorPos(cm.getCursor(false), Pos(0, 4)); - cm.setCursor(Pos(1, 0)); - cm.redo(); - eqCursorPos(cm.getCursor(true), Pos(0, 2)); - eqCursorPos(cm.getCursor(false), Pos(0, 2)); -}, {value: "abcdefgh\n"}); - -testCM("undoSelectionAsBefore", function(cm) { - cm.replaceSelection("abc", "around"); - cm.undo(); - cm.redo(); - eq(cm.getSelection(), "abc"); -}); - -testCM("selectionChangeConfusesHistory", function(cm) { - cm.replaceSelection("abc", null, "dontmerge"); - cm.operation(function() { - cm.setCursor(Pos(0, 0)); - cm.replaceSelection("abc", null, "dontmerge"); - }); - eq(cm.historySize().undo, 2); -}); - -testCM("markTextSingleLine", function(cm) { - forEach([{a: 0, b: 1, c: "", f: 2, t: 5}, - {a: 0, b: 4, c: "", f: 0, t: 2}, - {a: 1, b: 2, c: "x", f: 3, t: 6}, - {a: 4, b: 5, c: "", f: 3, t: 5}, - {a: 4, b: 5, c: "xx", f: 3, t: 7}, - {a: 2, b: 5, c: "", f: 2, t: 3}, - {a: 2, b: 5, c: "abcd", f: 6, t: 7}, - {a: 2, b: 6, c: "x", f: null, t: null}, - {a: 3, b: 6, c: "", f: null, t: null}, - {a: 0, b: 9, c: "hallo", f: null, t: null}, - {a: 4, b: 6, c: "x", f: 3, t: 4}, - {a: 4, b: 8, c: "", f: 3, t: 4}, - {a: 6, b: 6, c: "a", f: 3, t: 6}, - {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) { - cm.setValue("1234567890"); - var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"}); - cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b)); - var f = r.find(); - eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t); - }); -}); - -testCM("markTextMultiLine", function(cm) { - function p(v) { return v && Pos(v[0], v[1]); } - forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]}, - {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]}, - {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]}, - {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]}, - {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]}, - {a: [0, 6], b: [2, 4], c: "", f: [0, 5], t: [0, 7]}, - {a: [0, 6], b: [2, 4], c: "aa", f: [0, 5], t: [0, 9]}, - {a: [1, 2], b: [1, 8], c: "", f: [0, 5], t: [2, 5]}, - {a: [0, 5], b: [2, 5], c: "xx", f: null, t: null}, - {a: [0, 0], b: [2, 10], c: "x", f: null, t: null}, - {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]}, - {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]}, - {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]}, - {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]}, - {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) { - cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n"); - var r = cm.markText(Pos(0, 5), Pos(2, 5), - {className: "CodeMirror-matchingbracket"}); - cm.replaceRange(test.c, p(test.a), p(test.b)); - var f = r.find(); - eqCursorPos(f && f.from, p(test.f)); eqCursorPos(f && f.to, p(test.t)); - }); -}); - -testCM("markTextUndo", function(cm) { - var marker1, marker2, bookmark; - marker1 = cm.markText(Pos(0, 1), Pos(0, 3), - {className: "CodeMirror-matchingbracket"}); - marker2 = cm.markText(Pos(0, 0), Pos(2, 1), - {className: "CodeMirror-matchingbracket"}); - bookmark = cm.setBookmark(Pos(1, 5)); - cm.operation(function(){ - cm.replaceRange("foo", Pos(0, 2)); - cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0)); - }); - var v1 = cm.getValue(); - cm.setValue(""); - eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); - cm.undo(); - eqCursorPos(bookmark.find(), Pos(1, 5), "still there"); - cm.undo(); - var m1Pos = marker1.find(), m2Pos = marker2.find(); - eqCursorPos(m1Pos.from, Pos(0, 1)); eqCursorPos(m1Pos.to, Pos(0, 3)); - eqCursorPos(m2Pos.from, Pos(0, 0)); eqCursorPos(m2Pos.to, Pos(2, 1)); - eqCursorPos(bookmark.find(), Pos(1, 5)); - cm.redo(); cm.redo(); - eq(bookmark.find(), null); - cm.undo(); - eqCursorPos(bookmark.find(), Pos(1, 5)); - eq(cm.getValue(), v1); -}, {value: "1234\n56789\n00\n"}); - -testCM("markTextStayGone", function(cm) { - var m1 = cm.markText(Pos(0, 0), Pos(0, 1)); - cm.replaceRange("hi", Pos(0, 2)); - m1.clear(); - cm.undo(); - eq(m1.find(), null); -}, {value: "hello"}); - -testCM("markTextAllowEmpty", function(cm) { - var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false}); - is(m1.find()); - cm.replaceRange("x", Pos(0, 0)); - is(m1.find()); - cm.replaceRange("y", Pos(0, 2)); - is(m1.find()); - cm.replaceRange("z", Pos(0, 3), Pos(0, 4)); - is(!m1.find()); - var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false, - inclusiveLeft: true, - inclusiveRight: true}); - cm.replaceRange("q", Pos(0, 1), Pos(0, 2)); - is(m2.find()); - cm.replaceRange("", Pos(0, 0), Pos(0, 3)); - is(!m2.find()); - var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false}); - cm.replaceRange("a", Pos(0, 3)); - is(m3.find()); - cm.replaceRange("b", Pos(0, 1)); - is(!m3.find()); -}, {value: "abcde"}); - -testCM("markTextStacked", function(cm) { - var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); - var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); - cm.replaceRange("B", Pos(0, 1)); - is(m1.find() && m2.find()); -}, {value: "A"}); - -testCM("undoPreservesNewMarks", function(cm) { - cm.markText(Pos(0, 3), Pos(0, 4)); - cm.markText(Pos(1, 1), Pos(1, 3)); - cm.replaceRange("", Pos(0, 3), Pos(3, 1)); - var mBefore = cm.markText(Pos(0, 0), Pos(0, 1)); - var mAfter = cm.markText(Pos(0, 5), Pos(0, 6)); - var mAround = cm.markText(Pos(0, 2), Pos(0, 4)); - cm.undo(); - eqCursorPos(mBefore.find().from, Pos(0, 0)); - eqCursorPos(mBefore.find().to, Pos(0, 1)); - eqCursorPos(mAfter.find().from, Pos(3, 3)); - eqCursorPos(mAfter.find().to, Pos(3, 4)); - eqCursorPos(mAround.find().from, Pos(0, 2)); - eqCursorPos(mAround.find().to, Pos(3, 2)); - var found = cm.findMarksAt(Pos(2, 2)); - eq(found.length, 1); - eq(found[0], mAround); -}, {value: "aaaa\nbbbb\ncccc\ndddd"}); - -testCM("markClearBetween", function(cm) { - cm.setValue("aaa\nbbb\nccc\nddd\n"); - cm.markText(Pos(0, 0), Pos(2)); - cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2)); - eq(cm.findMarksAt(Pos(1, 1)).length, 0); -}); - -testCM("findMarksMiddle", function(cm) { - var mark = cm.markText(Pos(1, 1), Pos(3, 1)); - var found = cm.findMarks(Pos(2, 1), Pos(2, 2)); - eq(found.length, 1); - eq(found[0], mark); -}, {value: "line 0\nline 1\nline 2\nline 3"}); - -testCM("deleteSpanCollapsedInclusiveLeft", function(cm) { - var from = Pos(1, 0), to = Pos(1, 1); - var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true}); - // Delete collapsed span. - cm.replaceRange("", from, to); -}, {value: "abc\nX\ndef"}); - -testCM("markTextCSS", function(cm) { - function present() { - var spans = cm.display.lineDiv.getElementsByTagName("span"); - for (var i = 0; i < spans.length; i++) - if (spans[i].style.color && spans[i].textContent == "cdef") return true; - } - var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"}); - is(present()); - m.clear(); - is(!present()); -}, {value: "abcdefgh"}); - -testCM("markTextWithAttributes", function(cm) { - function present() { - var spans = cm.display.lineDiv.getElementsByTagName("span"); - for (var i = 0; i < spans.length; i++) - if (spans[i].getAttribute("label") == "label" && spans[i].textContent == "cdef") return true; - } - var m = cm.markText(Pos(0, 2), Pos(0, 6), {attributes: {label: "label"}}); - is(present()); - m.clear(); - is(!present()); -}, {value: "abcdefgh"}); - -testCM("bookmark", function(cm) { - function p(v) { return v && Pos(v[0], v[1]); } - forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]}, - {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]}, - {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]}, - {a: [1, 4], b: [1, 6], c: "", d: null}, - {a: [1, 5], b: [1, 6], c: "abc", d: [1, 5]}, - {a: [1, 6], b: [1, 8], c: "", d: [1, 5]}, - {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]}, - {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) { - cm.setValue("1234567890\n1234567890\n1234567890"); - var b = cm.setBookmark(p(test.bm) || Pos(1, 5)); - cm.replaceRange(test.c, p(test.a), p(test.b)); - eqCursorPos(b.find(), p(test.d)); - }); -}); - -testCM("bookmarkInsertLeft", function(cm) { - var br = cm.setBookmark(Pos(0, 2), {insertLeft: false}); - var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true}); - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi"); - eqCursorPos(br.find(), Pos(0, 2)); - eqCursorPos(bl.find(), Pos(0, 4)); - cm.replaceRange("", Pos(0, 4), Pos(0, 5)); - cm.replaceRange("", Pos(0, 2), Pos(0, 4)); - cm.replaceRange("", Pos(0, 1), Pos(0, 2)); - // Verify that deleting next to bookmarks doesn't kill them - eqCursorPos(br.find(), Pos(0, 1)); - eqCursorPos(bl.find(), Pos(0, 1)); -}, {value: "abcdef"}); - -testCM("bookmarkCursor", function(cm) { - var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)), - pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)), - pos41 = cm.cursorCoords(Pos(4, 1)); - cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true}); - cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true}); - cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")}); - cm.setBookmark(Pos(3, 0), {widget: document.createTextNode("→")}); - var new01 = cm.cursorCoords(Pos(0, 1)), new11 = cm.cursorCoords(Pos(1, 1)), - new20 = cm.cursorCoords(Pos(2, 0)), new30 = cm.cursorCoords(Pos(3, 0)); - near(new01.left, pos01.left, 1); - near(new01.top, pos01.top, 1); - is(new11.left > pos11.left, "at right, middle of line"); - near(new11.top == pos11.top, 1); - near(new20.left, pos20.left, 1); - near(new20.top, pos20.top, 1); - is(new30.left > pos30.left, "at right, empty line"); - near(new30.top, pos30, 1); - cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")}); - is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug"); -}, {value: "foo\nbar\n\n\nx\ny"}); - -testCM("multiBookmarkCursor", function(cm) { - var ms = [], m; - function add(insertLeft) { - for (var i = 0; i < 3; ++i) { - var node = document.createElement("span"); - node.innerHTML = "X"; - ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft})); - } - } - var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left; - add(true); - near(base1, cm.cursorCoords(Pos(0, 1)).left, 1); - while (m = ms.pop()) m.clear(); - add(false); - near(base4, cm.cursorCoords(Pos(0, 1)).left, 1); -}, {value: "abcdefg"}); - -testCM("getAllMarks", function(cm) { - addDoc(cm, 10, 10); - var m1 = cm.setBookmark(Pos(0, 2)); - var m2 = cm.markText(Pos(0, 2), Pos(3, 2)); - var m3 = cm.markText(Pos(1, 2), Pos(1, 8)); - var m4 = cm.markText(Pos(8, 0), Pos(9, 0)); - eq(cm.getAllMarks().length, 4); - m1.clear(); - m3.clear(); - eq(cm.getAllMarks().length, 2); -}); - -testCM("setValueClears", function(cm) { - cm.addLineClass(0, "wrap", "foo"); - var mark = cm.markText(Pos(0, 0), Pos(1, 1), {inclusiveLeft: true, inclusiveRight: true}); - cm.setValue("foo"); - is(!cm.lineInfo(0).wrapClass); - is(!mark.find()); -}, {value: "a\nb"}); - -testCM("bug577", function(cm) { - cm.setValue("a\nb"); - cm.clearHistory(); - cm.setValue("fooooo"); - cm.undo(); -}); - -testCM("scrollSnap", function(cm) { - cm.setSize(100, 100); - addDoc(cm, 200, 200); - cm.setCursor(Pos(100, 180)); - var info = cm.getScrollInfo(); - is(info.left > 0 && info.top > 0); - cm.setCursor(Pos(0, 0)); - info = cm.getScrollInfo(); - is(info.left == 0 && info.top == 0, "scrolled clean to top"); - cm.setCursor(Pos(100, 180)); - cm.setCursor(Pos(199, 0)); - info = cm.getScrollInfo(); - is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom"); -}); - -testCM("scrollIntoView", function(cm) { - function test(line, ch, msg) { - var pos = Pos(line, ch); - cm.scrollIntoView(pos); - var outer = cm.getWrapperElement().getBoundingClientRect(); - var box = cm.charCoords(pos, "window"); - is(box.left >= outer.left, msg + " (left)"); - is(box.right <= outer.right, msg + " (right)"); - is(box.top >= outer.top, msg + " (top)"); - is(box.bottom <= outer.bottom, msg + " (bottom)"); - } - addDoc(cm, 200, 200); - test(199, 199, "bottom right"); - test(0, 0, "top left"); - test(100, 100, "center"); - test(199, 0, "bottom left"); - test(0, 199, "top right"); - test(100, 100, "center again"); -}); - -testCM("scrollBackAndForth", function(cm) { - addDoc(cm, 1, 200); - cm.operation(function() { - cm.scrollIntoView(Pos(199, 0)); - cm.scrollIntoView(Pos(4, 0)); - }); - is(cm.getScrollInfo().top > 0); -}); - -testCM("selectAllNoScroll", function(cm) { - addDoc(cm, 1, 200); - cm.execCommand("selectAll"); - eq(cm.getScrollInfo().top, 0); - cm.setCursor(199); - cm.execCommand("selectAll"); - is(cm.getScrollInfo().top > 0); -}); - -testCM("selectionPos", function(cm) { - if (cm.getOption("inputStyle") != "textarea") return; - cm.setSize(100, 100); - addDoc(cm, 200, 100); - cm.setSelection(Pos(1, 100), Pos(98, 100)); - var lineWidth = cm.charCoords(Pos(0, 200), "local").left; - var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100; - cm.scrollTo(0, 0); - var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); - var outer = cm.getWrapperElement().getBoundingClientRect(); - var sawMiddle, sawTop, sawBottom; - for (var i = 0, e = selElt.length; i < e; ++i) { - var box = selElt[i].getBoundingClientRect(); - var atLeft = box.left - outer.left < 30; - var width = box.right - box.left; - var atRight = box.right - outer.left > .8 * lineWidth; - if (atLeft && atRight) { - sawMiddle = true; - is(box.bottom - box.top > 90 * lineHeight, "middle high"); - is(width > .9 * lineWidth, "middle wide"); - } else { - is(width > .4 * lineWidth, "top/bot wide enough"); - is(width < .6 * lineWidth, "top/bot slim enough"); - if (atLeft) { - sawBottom = true; - is(box.top - outer.top > 96 * lineHeight, "bot below"); - } else if (atRight) { - sawTop = true; - is(box.top - outer.top < 2.1 * lineHeight, "top above"); - } - } - } - is(sawTop && sawBottom && sawMiddle, "all parts"); -}, null); - -testCM("restoreHistory", function(cm) { - cm.setValue("abc\ndef"); - cm.replaceRange("hello", Pos(1, 0), Pos(1)); - cm.replaceRange("goop", Pos(0, 0), Pos(0)); - cm.undo(); - var storedVal = cm.getValue(), storedHist = cm.getHistory(); - if (window.JSON) storedHist = JSON.parse(JSON.stringify(storedHist)); - eq(storedVal, "abc\nhello"); - cm.setValue(""); - cm.clearHistory(); - eq(cm.historySize().undo, 0); - cm.setValue(storedVal); - cm.setHistory(storedHist); - cm.redo(); - eq(cm.getValue(), "goop\nhello"); - cm.undo(); cm.undo(); - eq(cm.getValue(), "abc\ndef"); -}); - -testCM("doubleScrollbar", function(cm) { - var dummy = document.body.appendChild(document.createElement("p")); - dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px"; - var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth; - document.body.removeChild(dummy); - if (scrollbarWidth < 2) return; - cm.setSize(null, 100); - addDoc(cm, 1, 300); - var wrap = cm.getWrapperElement(); - is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth * 1.5); -}); - -testCM("weirdLinebreaks", function(cm) { - cm.setValue("foo\nbar\rbaz\r\nquux\n\rplop"); - is(cm.getValue(), "foo\nbar\nbaz\nquux\n\nplop"); - is(cm.lineCount(), 6); - cm.setValue("\n\n"); - is(cm.lineCount(), 3); -}); - -testCM("setSize", function(cm) { - cm.setSize(100, 100); - var wrap = cm.getWrapperElement(); - is(wrap.offsetWidth, 100); - is(wrap.offsetHeight, 100); - cm.setSize("100%", "3em"); - is(wrap.style.width, "100%"); - is(wrap.style.height, "3em"); - cm.setSize(null, 40); - is(wrap.style.width, "100%"); - is(wrap.style.height, "40px"); -}); - -function foldLines(cm, start, end, autoClear) { - return cm.markText(Pos(start, 0), Pos(end - 1), { - inclusiveLeft: true, - inclusiveRight: true, - collapsed: true, - clearOnEnter: autoClear - }); -} - -testCM("collapsedLines", function(cm) { - addDoc(cm, 4, 10); - var range = foldLines(cm, 4, 5), cleared = 0; - CodeMirror.on(range, "clear", function() {cleared++;}); - cm.setCursor(Pos(3, 0)); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(5, 0)); - cm.replaceRange("abcdefg", Pos(3, 0), Pos(3)); - cm.setCursor(Pos(3, 6)); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(5, 4)); - cm.replaceRange("ab", Pos(3, 0), Pos(3)); - cm.setCursor(Pos(3, 2)); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(5, 2)); - cm.operation(function() {range.clear(); range.clear();}); - eq(cleared, 1); -}); - -testCM("collapsedRangeCoordsChar", function(cm) { - var pos_1_3 = cm.charCoords(Pos(1, 3)); - pos_1_3.left += 2; pos_1_3.top += 2; - var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; - var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts); - eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); - m1.clear(); - var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true}); - var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true}); - eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); - m1.clear(); m2.clear(); - var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts); - eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); -}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"}); - -testCM("collapsedRangeBetweenLinesSelected", function(cm) { - if (cm.getOption("inputStyle") != "textarea") return; - var widget = document.createElement("span"); - widget.textContent = "\u2194"; - cm.markText(Pos(0, 3), Pos(1, 0), {replacedWith: widget}); - cm.setSelection(Pos(0, 3), Pos(1, 0)); - var selElts = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); - for (var i = 0, w = 0; i < selElts.length; i++) - w += selElts[i].offsetWidth; - is(w > 0); -}, {value: "one\ntwo"}); - -testCM("randomCollapsedRanges", function(cm) { - addDoc(cm, 20, 500); - cm.operation(function() { - for (var i = 0; i < 200; i++) { - var start = Pos(Math.floor(Math.random() * 500), Math.floor(Math.random() * 20)); - if (i % 4) - try { cm.markText(start, Pos(start.line + 2, 1), {collapsed: true}); } - catch(e) { if (!/overlapping/.test(String(e))) throw e; } - else - cm.markText(start, Pos(start.line, start.ch + 4), {"className": "foo"}); - } - }); -}); - -testCM("hiddenLinesAutoUnfold", function(cm) { - var range = foldLines(cm, 1, 3, true), cleared = 0; - CodeMirror.on(range, "clear", function() {cleared++;}); - cm.setCursor(Pos(3, 0)); - eq(cleared, 0); - cm.execCommand("goCharLeft"); - eq(cleared, 1); - range = foldLines(cm, 1, 3, true); - CodeMirror.on(range, "clear", function() {cleared++;}); - eqCursorPos(cm.getCursor(), Pos(3, 0)); - cm.setCursor(Pos(0, 3)); - cm.execCommand("goCharRight"); - eq(cleared, 2); -}, {value: "abc\ndef\nghi\njkl"}); - -testCM("hiddenLinesSelectAll", function(cm) { // Issue #484 - addDoc(cm, 4, 20); - foldLines(cm, 0, 10); - foldLines(cm, 11, 20); - CodeMirror.commands.selectAll(cm); - eqCursorPos(cm.getCursor(true), Pos(10, 0)); - eqCursorPos(cm.getCursor(false), Pos(10, 4)); -}); - -testCM("clickFold", function(cm) { // Issue #5392 - cm.setValue("foo { bar }") - var widget = document.createElement("span") - widget.textContent = "<>" - cm.markText(Pos(0, 5), Pos(0, 10), {replacedWith: widget}) - var after = cm.charCoords(Pos(0, 10)) - var foundOn = cm.coordsChar({left: after.left - 1, top: after.top + 4}) - is(foundOn.ch <= 5 || foundOn.ch >= 10, "Position is not inside the folded range") -}) - -testCM("everythingFolded", function(cm) { - addDoc(cm, 2, 2); - function enterPress() { - cm.triggerOnKeyDown({type: "keydown", keyCode: 13, preventDefault: function(){}, stopPropagation: function(){}}); - } - var fold = foldLines(cm, 0, 2); - enterPress(); - eq(cm.getValue(), "xx\nxx"); - fold.clear(); - fold = foldLines(cm, 0, 2, true); - eq(fold.find(), null); - enterPress(); - eq(cm.getValue(), "\nxx\nxx"); -}); - -testCM("structuredFold", function(cm) { - addDoc(cm, 4, 8); - var range = cm.markText(Pos(1, 2), Pos(6, 2), { - replacedWith: document.createTextNode("Q") - }); - cm.setCursor(0, 3); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(6, 2)); - CodeMirror.commands.goCharLeft(cm); - eqCharPos(cm.getCursor(), Pos(1, 2)); - CodeMirror.commands.delCharAfter(cm); - eq(cm.getValue(), "xxxx\nxxxx\nxxxx"); - addDoc(cm, 4, 8); - range = cm.markText(Pos(1, 2), Pos(6, 2), { - replacedWith: document.createTextNode("M"), - clearOnEnter: true - }); - var cleared = 0; - CodeMirror.on(range, "clear", function(){++cleared;}); - cm.setCursor(0, 3); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(6, 2)); - CodeMirror.commands.goCharLeft(cm); - eqCharPos(cm.getCursor(), Pos(6, 1)); - eq(cleared, 1); - range.clear(); - eq(cleared, 1); - range = cm.markText(Pos(1, 2), Pos(6, 2), { - replacedWith: document.createTextNode("Q"), - clearOnEnter: true - }); - range.clear(); - cm.setCursor(1, 2); - CodeMirror.commands.goCharRight(cm); - eqCharPos(cm.getCursor(), Pos(1, 3)); - range = cm.markText(Pos(2, 0), Pos(4, 4), { - replacedWith: document.createTextNode("M") - }); - cm.setCursor(1, 0); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(2, 0)); -}, null); - -testCM("nestedFold", function(cm) { - addDoc(cm, 10, 3); - function fold(ll, cl, lr, cr) { - return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true}); - } - var inner1 = fold(0, 6, 1, 3), inner2 = fold(0, 2, 1, 8), outer = fold(0, 1, 2, 3), inner0 = fold(0, 5, 0, 6); - cm.setCursor(0, 1); - CodeMirror.commands.goCharRight(cm); - eqCursorPos(cm.getCursor(), Pos(2, 3)); - inner0.clear(); - CodeMirror.commands.goCharLeft(cm); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - outer.clear(); - CodeMirror.commands.goCharRight(cm); - eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); - CodeMirror.commands.goCharRight(cm); - eqCursorPos(cm.getCursor(), Pos(1, 8)); - inner2.clear(); - CodeMirror.commands.goCharLeft(cm); - eqCursorPos(cm.getCursor(), Pos(1, 7, "after")); - cm.setCursor(0, 5); - CodeMirror.commands.goCharRight(cm); - eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); - CodeMirror.commands.goCharRight(cm); - eqCursorPos(cm.getCursor(), Pos(1, 3)); -}); - -testCM("badNestedFold", function(cm) { - addDoc(cm, 4, 4); - cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true}); - var caught; - try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});} - catch(e) {caught = e;} - is(caught instanceof Error, "no error"); - is(/overlap/i.test(caught.message), "wrong error"); -}); - -testCM("nestedFoldOnSide", function(cm) { - var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true}); - var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}); - cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear(); - try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); } - catch(e) { var caught = e; } - is(caught && /overlap/i.test(caught.message)); - var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}); - var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true}); - m1.clear(); m4.clear(); - m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true}); - cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear(); - try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); } - catch(e) { var caught = e; } - is(caught && /overlap/i.test(caught.message)); -}, {value: "ab\ncd\ef"}); - -testCM("editInFold", function(cm) { - addDoc(cm, 4, 6); - var m = cm.markText(Pos(1, 2), Pos(3, 2), {collapsed: true}); - cm.replaceRange("", Pos(0, 0), Pos(1, 3)); - cm.replaceRange("", Pos(2, 1), Pos(3, 3)); - cm.replaceRange("a\nb\nc\nd", Pos(0, 1), Pos(1, 0)); - cm.cursorCoords(Pos(0, 0)); -}); - -testCM("wrappingInlineWidget", function(cm) { - cm.setSize("11em"); - var w = document.createElement("span"); - w.style.color = "red"; - w.innerHTML = "one two three four"; - cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w}); - var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10)); - is(cur0.top < cur1.top); - is(cur0.bottom < cur1.bottom); - var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9)); - eq(curL.top, cur0.top); - eq(curL.bottom, cur0.bottom); - eq(curR.top, cur1.top); - eq(curR.bottom, cur1.bottom); - cm.replaceRange("", Pos(0, 9), Pos(0)); - curR = cm.cursorCoords(Pos(0, 9)); - eq(curR.top, cur1.top); - eq(curR.bottom, cur1.bottom); -}, {value: "1 2 3 xxx 4", lineWrapping: true}); - -testCM("showEmptyWidgetSpan", function(cm) { - var marker = cm.markText(Pos(0, 2), Pos(0, 2), { - clearWhenEmpty: false, - replacedWith: document.createTextNode("X") - }); - var text = cm.display.view[0].text; - eq(text.textContent || text.innerText, "abXc"); -}, {value: "abc"}); - -testCM("changedInlineWidget", function(cm) { - cm.setSize("10em"); - var w = document.createElement("span"); - w.innerHTML = "x"; - var m = cm.markText(Pos(0, 4), Pos(0, 5), {replacedWith: w}); - w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed"; - m.changed(); - var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; - is(hScroll.scrollWidth > hScroll.clientWidth); -}, {value: "hello there"}); - -testCM("changedBookmark", function(cm) { - cm.setSize("10em"); - var w = document.createElement("span"); - w.innerHTML = "x"; - var m = cm.setBookmark(Pos(0, 4), {widget: w}); - w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed"; - m.changed(); - var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0]; - is(hScroll.scrollWidth > hScroll.clientWidth); -}, {value: "abcdefg"}); - -testCM("inlineWidget", function(cm) { - var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); - cm.setCursor(0, 2); - CodeMirror.commands.goLineDown(cm); - eqCharPos(cm.getCursor(), Pos(1, 4)); - cm.setCursor(0, 2); - cm.replaceSelection("hi"); - eqCharPos(w.find(), Pos(0, 2)); - cm.setCursor(0, 1); - cm.replaceSelection("ay"); - eqCharPos(w.find(), Pos(0, 4)); - eq(cm.getLine(0), "uayuhiuu"); -}, {value: "uuuu\nuuuuuu"}); - -testCM("wrappingAndResizing", function(cm) { - cm.setSize(null, "auto"); - cm.setOption("lineWrapping", true); - var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight; - var doc = "xxx xxx xxx xxx xxx"; - cm.setValue(doc); - for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) { - cm.setSize(w); - if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) { - if (step == 10) { w -= 10; step = 1; } - else break; - } - } - // Ensure that putting the cursor at the end of the maximally long - // line doesn't cause wrapping to happen. - cm.setCursor(Pos(0, doc.length)); - eq(wrap.offsetHeight, h0); - cm.replaceSelection("x"); - is(wrap.offsetHeight > h0, "wrapping happens"); - // Now add a max-height and, in a document consisting of - // almost-wrapped lines, go over it so that a scrollbar appears. - cm.setValue(doc + "\n" + doc + "\n"); - cm.getScrollerElement().style.maxHeight = "100px"; - cm.replaceRange("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n!\n", Pos(2, 0)); - forEach([Pos(0, doc.length), Pos(0, doc.length - 1), - Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)], - function(pos) { - var coords = cm.charCoords(pos); - eqCharPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5})); - }); -}, null, ie_lt8); - -testCM("measureEndOfLine", function(cm) { - cm.setSize(null, "auto"); - var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; - var lh = inner.offsetHeight; - for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { - cm.setSize(w); - if (inner.offsetHeight < 2.5 * lh) { - if (step == 10) { w -= 10; step = 1; } - else break; - } - } - cm.setValue(cm.getValue() + "\n\n"); - var endPos = cm.charCoords(Pos(0, 18), "local"); - is(endPos.top > lh * .8, "not at top"); - is(endPos.left > w - 20, "at right"); - endPos = cm.charCoords(Pos(0, 18)); - eqCursorPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18, "before")); - - var wrapPos = cm.cursorCoords(Pos(0, 9, "before")); - is(wrapPos.top < endPos.top, "wrapPos is actually in first line"); - eqCursorPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before")); -}, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8 || opera_lt10); - -testCM("measureWrappedEndOfLine", function(cm) { - cm.setSize(null, "auto"); - var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; - var lh = inner.offsetHeight; - for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { - cm.setSize(w); - if (inner.offsetHeight < 2.5 * lh) { - if (step == 10) { w -= 10; step = 1; } - else break; - } - } - for (var i = 0; i < 3; ++i) { - var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) - endPos.left += w; // Add width of editor just to be sure that we are behind last character - eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); - endPos.left += w * 100; - eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); - cm.setValue("0123456789abcابجابجابجابج"); - if (i == 1) { - var node = document.createElement("div"); - node.innerHTML = "hi"; node.style.height = "30px"; - cm.addLineWidget(0, node, {above: true}); - } - } -}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); - -testCM("measureEndOfLineBidi", function(cm) { - eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after")) -}, {value: "إإإإuuuuإإإإ"}) - -testCM("measureWrappedBidiLevel2", function(cm) { - cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60) - var c9 = cm.charCoords(Pos(0, 9)) - eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9)) -}, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true}) - -testCM("measureWrappedBeginOfLine", function(cm) { - cm.setSize(null, "auto"); - var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; - var lh = inner.offsetHeight; - for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { - cm.setSize(w); - if (inner.offsetHeight < 2.5 * lh) { - if (step == 10) { w -= 10; step = 1; } - else break; - } - } - var beginOfSecondLine = Pos(0, 13, "after"); - for (var i = 0; i < 2; ++i) { - var beginPos = cm.charCoords(Pos(0, 0)); - beginPos.left -= w; - eqCursorPos(cm.coordsChar(beginPos), Pos(0, 0, "after")); - beginPos = cm.cursorCoords(beginOfSecondLine); - beginPos.left = 0; - eqCursorPos(cm.coordsChar(beginPos), beginOfSecondLine); - cm.setValue("0123456789abcابجابجابجابج"); - beginOfSecondLine = Pos(0, 25, "before"); - } -}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}); - -testCM("scrollVerticallyAndHorizontally", function(cm) { - if (cm.getOption("inputStyle") != "textarea") return; - cm.setSize(100, 100); - addDoc(cm, 40, 40); - cm.setCursor(39); - var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0]; - is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one"); - var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect(); - var editorBox = wrap.getBoundingClientRect(); - is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight, - "bottom line visible"); -}, {lineNumbers: true}); - -testCM("moveVstuck", function(cm) { - var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; - var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n"; - cm.setValue(val); - for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) { - cm.setSize(w); - if (lines.offsetHeight <= 3.5 * h0) break; - } - cm.setCursor(Pos(0, val.length - 1)); - cm.moveV(-1, "line"); - eqCursorPos(cm.getCursor(), Pos(0, 27, "before")); - is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line"); -}, {lineWrapping: true}, ie_lt8 || opera_lt10); - -testCM("collapseOnMove", function(cm) { - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goLineUp"); - is(!cm.somethingSelected()); - eqCharPos(cm.getCursor(), Pos(0, 1)); - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goPageDown"); - is(!cm.somethingSelected()); - eqCharPos(cm.getCursor(), Pos(2, 4)); - cm.execCommand("goLineUp"); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(0, 4)); - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goCharLeft"); - is(!cm.somethingSelected()); - eqCharPos(cm.getCursor(), Pos(0, 1)); -}, {value: "aaaaa\nb\nccccc"}); - -testCM("clickTab", function(cm) { - var p0 = cm.charCoords(Pos(0, 0)); - eqCharPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0)); - eqCharPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1)); -}, {value: "\t\n\n", lineWrapping: true, tabSize: 8}); - -testCM("verticalScroll", function(cm) { - cm.setSize(100, 200); - cm.setValue("foo\nbar\nbaz\n"); - var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth; - cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); - is(sc.scrollWidth > baseWidth, "scrollbar present"); - cm.replaceRange("foo", Pos(0, 0), Pos(0)); - eq(sc.scrollWidth, baseWidth, "scrollbar gone"); - cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); - cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1)); - is(sc.scrollWidth > baseWidth, "present again"); - var curWidth = sc.scrollWidth; - cm.replaceRange("foo", Pos(0, 0), Pos(0)); - is(sc.scrollWidth < curWidth, "scrollbar smaller"); - is(sc.scrollWidth > baseWidth, "but still present"); -}); - -testCM("extraKeys", function(cm) { - var outcome; - function fakeKey(expected, code, props) { - if (typeof code == "string") code = code.charCodeAt(0); - var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}}; - if (props) for (var n in props) e[n] = props[n]; - outcome = null; - cm.triggerOnKeyDown(e); - eq(outcome, expected); - } - CodeMirror.commands.testCommand = function() {outcome = "tc";}; - CodeMirror.commands.goTestCommand = function() {outcome = "gtc";}; - cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";}, - "X": function() {outcome = "x";}, - "Ctrl-Alt-U": function() {outcome = "cau";}, - "End": "testCommand", - "Home": "goTestCommand", - "Tab": false}); - fakeKey(null, "U"); - fakeKey("cau", "U", {ctrlKey: true, altKey: true}); - fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true}); - fakeKey("x", "X"); - fakeKey("sx", "X", {shiftKey: true}); - fakeKey("tc", 35); - fakeKey(null, 35, {shiftKey: true}); - fakeKey("gtc", 36); - fakeKey("gtc", 36, {shiftKey: true}); - fakeKey(null, 9); -}, null, window.opera && mac); - -testCM("wordMovementCommands", function(cm) { - cm.execCommand("goWordLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); - cm.execCommand("goWordLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 5, "after")); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqCursorPos(cm.getCursor(), Pos(0, 12, "before")); - cm.execCommand("goWordLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 9, "after")); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqCursorPos(cm.getCursor(), Pos(0, 24, "before")); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqCursorPos(cm.getCursor(), Pos(1, 9, "before")); - cm.execCommand("goWordRight"); - eqCursorPos(cm.getCursor(), Pos(1, 13, "before")); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqCharPos(cm.getCursor(), Pos(2, 0)); -}, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"}); - -testCM("groupMovementCommands", function(cm) { - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(0, 10, "before")); - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 7, "after")); - cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(0, 15, "before")); - cm.setCursor(Pos(0, 17)); - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 14, "after")); - cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(0, 20, "before")); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(1, 2, "before")); - cm.execCommand("goGroupRight"); - eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); - cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 20, "after")); - cm.execCommand("goGroupLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); -}, {value: "booo ba---quux. ffff\n abc d"}); - -testCM("groupsAndWhitespace", function(cm) { - var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11), - Pos(1, 0), Pos(1, 2), Pos(1, 5)]; - for (var i = 1; i < positions.length; i++) { - cm.execCommand("goGroupRight"); - eqCharPos(cm.getCursor(), positions[i]); - } - for (var i = positions.length - 2; i >= 0; i--) { - cm.execCommand("goGroupLeft"); - eqCharPos(cm.getCursor(), i == 2 ? Pos(0, 6, "before") : positions[i]); - } -}, {value: " foo +++ \n bar"}); - -testCM("charMovementCommands", function(cm) { - cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); - cm.setCursor(Pos(1, 0)); - cm.execCommand("goColumnLeft"); - eqCursorPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); - cm.execCommand("goColumnRight"); - eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); - cm.execCommand("goLineEnd"); - eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); - cm.execCommand("goLineStartSmart"); - eqCursorPos(cm.getCursor(), Pos(1, 1, "after")); - cm.execCommand("goLineStartSmart"); - eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); - cm.setCursor(Pos(2, 0)); - cm.execCommand("goCharRight"); cm.execCommand("goColumnRight"); - eqCursorPos(cm.getCursor(), Pos(2, 0)); -}, {value: "line1\n ine2\n"}); - -testCM("verticalMovementCommands", function(cm) { - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(1, 0)); - cm.setCursor(Pos(1, 12)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(2, 5)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(3, 0)); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(2, 5)); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(1, 12)); - cm.execCommand("goPageDown"); - eqCharPos(cm.getCursor(), Pos(5, 0)); - cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(5, 0)); - cm.execCommand("goPageUp"); - eqCharPos(cm.getCursor(), Pos(0, 0)); -}, {value: "line1\nlong long line2\nline3\n\nline5\n"}); - -testCM("verticalMovementCommandsWrapping", function(cm) { - cm.setSize(120); - cm.setCursor(Pos(0, 5)); - cm.execCommand("goLineDown"); - eq(cm.getCursor().line, 0); - is(cm.getCursor().ch > 5, "moved beyond wrap"); - for (var i = 0; ; ++i) { - is(i < 20, "no endless loop"); - cm.execCommand("goLineDown"); - var cur = cm.getCursor(); - if (cur.line == 1) eq(cur.ch, 5); - if (cur.line == 2) { eq(cur.ch, 1); break; } - } -}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk", - lineWrapping: true}); - -testCM("verticalMovementCommandsSingleLine", function(cm) { - cm.display.wrapper.style.height = "auto"; - cm.refresh(); - cm.execCommand("goLineUp"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); - cm.setCursor(Pos(0, 5)); - cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); - cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); - cm.execCommand("goLineUp"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goLineUp"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goPageDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); - cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); - cm.execCommand("goPageUp"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.setCursor(Pos(0, 5)); - cm.execCommand("goPageUp"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.setCursor(Pos(0, 5)); - cm.execCommand("goPageDown"); - eqCursorPos(cm.getCursor(), Pos(0, 11)); -}, {value: "single line"}); - - -testCM("rtlMovement", function(cm) { - if (cm.getOption("inputStyle") != "textarea") return; - forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", - "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق", - "", "يتم السحب في 05 فبراير 2014"], function(line) { - cm.setValue(line + "\n"); cm.execCommand("goLineStart"); - var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0]; - var cursor = cursors.firstChild; - var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; - for (var i = 0; i <= line.length; ++i) { - cm.execCommand("goCharRight"); - cursor = cursors.firstChild; - if (i == line.length) is(cursor.offsetTop > prevY, "next line"); - else is(cursor.offsetLeft > prevX, "moved right"); - prevX = cursor.offsetLeft; prevY = cursor.offsetTop; - } - cm.setCursor(0, 0); cm.execCommand("goLineEnd"); - prevX = cursors.firstChild.offsetLeft; - for (var i = 0; i < line.length; ++i) { - cm.execCommand("goCharLeft"); - cursor = cursors.firstChild; - is(cursor.offsetLeft < prevX, "moved left"); - prevX = cursor.offsetLeft; - } - }); -}, null, ie_lt9); - -// Verify that updating a line clears its bidi ordering -testCM("bidiUpdate", function(cm) { - cm.setCursor(Pos(0, 2, "before")); - cm.replaceSelection("خحج", "start"); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); -}, {value: "abcd\n"}); - -testCM("movebyTextUnit", function(cm) { - cm.setValue("בְּרֵאשִ\nééé́\n"); - cm.execCommand("goLineStart"); - for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 0, "after")); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(1, 4, "before")); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(1, 7, "before")); -}); - -testCM("lineChangeEvents", function(cm) { - addDoc(cm, 3, 5); - var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"]; - for (var i = 0; i < 5; ++i) { - CodeMirror.on(cm.getLineHandle(i), "delete", function(i) { - return function() {log.push("del " + i);}; - }(i)); - CodeMirror.on(cm.getLineHandle(i), "change", function(i) { - return function() {log.push("ch " + i);}; - }(i)); - } - cm.replaceRange("x", Pos(0, 1)); - cm.replaceRange("xy", Pos(1, 1), Pos(2)); - cm.replaceRange("foo\nbar", Pos(0, 1)); - cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount())); - eq(log.length, want.length, "same length"); - for (var i = 0; i < log.length; ++i) - eq(log[i], want[i]); -}); - -testCM("scrollEntirelyToRight", function(cm) { - if (cm.getOption("inputStyle") != "textarea") return; - addDoc(cm, 500, 2); - cm.setCursor(Pos(0, 500)); - var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0]; - is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left); -}); - -testCM("lineWidgets", function(cm) { - addDoc(cm, 500, 3); - var last = cm.charCoords(Pos(2, 0)); - var node = document.createElement("div"); - node.innerHTML = "hi"; - var widget = cm.addLineWidget(1, node); - is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space"); - cm.setCursor(Pos(1, 1)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(1, 1)); -}); - -testCM("lineWidgetFocus", function(cm) { - var place = document.getElementById("testground"); - place.className = "offscreen"; - try { - addDoc(cm, 500, 10); - var node = document.createElement("input"); - var widget = cm.addLineWidget(1, node); - node.focus(); - eq(document.activeElement, node); - cm.replaceRange("new stuff", Pos(1, 0)); - eq(document.activeElement, node); - } finally { - place.className = ""; - } -}); - -testCM("lineWidgetCautiousRedraw", function(cm) { - var node = document.createElement("div"); - node.innerHTML = "hahah"; - var w = cm.addLineWidget(0, node); - var redrawn = false; - w.on("redraw", function() { redrawn = true; }); - cm.replaceSelection("0"); - is(!redrawn); -}, {value: "123\n456"}); - - -var knownScrollbarWidth; -function scrollbarWidth(measure) { - if (knownScrollbarWidth != null) return knownScrollbarWidth; - var div = document.createElement('div'); - div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll"; - document.body.appendChild(div); - knownScrollbarWidth = div.offsetHeight - div.clientHeight; - document.body.removeChild(div); - return knownScrollbarWidth || 0; -} - -testCM("lineWidgetChanged", function(cm) { - addDoc(cm, 2, 300); - var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2; - cm.setOption('lineNumbers', true); - cm.setSize(600, cm.defaultTextHeight() * 50); - cm.scrollTo(null, cm.heightAtLine(125, "local")); - - var expectedWidgetHeight = 60; - var expectedLinesInWidget = 3; - function w() { - var node = document.createElement("div"); - // we use these children with just under half width of the line to check measurements are made with correct width - // when placed in the measure div. - // If the widget is measured at a width much narrower than it is displayed at, the underHalf children will span two lines and break the test. - // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test. - // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right. - // It may also be worthwhile to check this for non-coverGutter widgets. - // Visually: - // Good: - // | ------------- display width ------------- | - // | ------- widget-width when measured ------ | - // | | -- under-half -- | | -- under-half -- | | - // | | --- over-half --- | | - // | | --- over-half --- | | - // Height: measured as 3 lines, same as it will be when actually displayed - - // Bad (too narrow): - // | ------------- display width ------------- | - // | ------ widget-width when measured ----- | < -- uh oh - // | | -- under-half -- | | - // | | -- under-half -- | | < -- when measured, shoved to next line - // | | --- over-half --- | | - // | | --- over-half --- | | - // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines! - - // Bad (too wide): - // | ------------- display width ------------- | - // | -------- widget-width when measured ------- | < -- uh oh - // | | -- under-half -- | | -- under-half -- | | - // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line - // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines! - - var barelyUnderHalfWidthHtml = '
'; - var barelyOverHalfWidthHtml = '
'; - node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml); - node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;"; - return node; - } - var info0 = cm.getScrollInfo(); - var w0 = cm.addLineWidget(0, w(), { coverGutter: true }); - var w150 = cm.addLineWidget(150, w(), { coverGutter: true }); - var w300 = cm.addLineWidget(300, w(), { coverGutter: true }); - var info1 = cm.getScrollInfo(); - eq(info0.height + (3 * expectedWidgetHeight), info1.height); - eq(info0.top + expectedWidgetHeight, info1.top); - expectedWidgetHeight = 12; - w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px"; - w0.changed(); w150.changed(); w300.changed(); - var info2 = cm.getScrollInfo(); - eq(info0.height + (3 * expectedWidgetHeight), info2.height); - eq(info0.top + expectedWidgetHeight, info2.top); -}); - -testCM("lineWidgetIssue5486", function(cm) { - // [prepare] - // 2nd line is combined to 1st line due to markText - // 2nd line has a lineWidget below - - cm.setValue("Lorem\nIpsue\nDollar") - - var el = document.createElement('div') - el.style.height='50px' - el.textContent = '[[LINE WIDGET]]' - - var lineWidget = cm.addLineWidget(1, el, { - above: false, - coverGutter: false, - noHScroll: false, - showIfHidden: false, - }) - - var marker = document.createElement('span') - marker.textContent = '[--]' - - cm.markText({line:0, ch: 1}, {line:1, ch: 4}, { - replacedWith: marker - }) - - // before resizing the lineWidget, measure 3rd line position - - var measure_1 = Math.round(cm.charCoords({line:2, ch:0}).top) - - // resize lineWidget, height + 50 px - - el.style.height='100px' - el.textContent += "\nlineWidget size changed.\nTry moving cursor to line 3?" - - lineWidget.changed() - - // re-measure 3rd line position - var measure_2 = Math.round(cm.charCoords({line:2, ch:0}).top) - eq(measure_2, measure_1 + 50) - - // (extra test) - // - // add char to the right of the folded marker - // and re-measure 3rd line position - - cm.replaceRange('-', {line:1, ch: 5}) - var measure_3 = Math.round(cm.charCoords({line:2, ch:0}).top) - eq(measure_3, measure_2) -}); - -testCM("getLineNumber", function(cm) { - addDoc(cm, 2, 20); - var h1 = cm.getLineHandle(1); - eq(cm.getLineNumber(h1), 1); - cm.replaceRange("hi\nbye\n", Pos(0, 0)); - eq(cm.getLineNumber(h1), 3); - cm.setValue(""); - eq(cm.getLineNumber(h1), null); -}); - -testCM("jumpTheGap", function(cm) { - var longLine = "abcdef ghiklmnop qrstuvw xyz "; - longLine += longLine; longLine += longLine; longLine += longLine; - cm.replaceRange(longLine, Pos(2, 0), Pos(2)); - cm.setSize("200px", null); - cm.getWrapperElement().style.lineHeight = 2; - cm.refresh(); - cm.setCursor(Pos(0, 1)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineDown"); - eq(cm.getCursor().line, 2); - is(cm.getCursor().ch > 1); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(1, 1)); - var node = document.createElement("div"); - node.innerHTML = "hi"; node.style.height = "30px"; - cm.addLineWidget(0, node); - cm.addLineWidget(1, node.cloneNode(true), {above: true}); - cm.setCursor(Pos(0, 2)); - cm.execCommand("goLineDown"); - eqCharPos(cm.getCursor(), Pos(1, 2)); - cm.execCommand("goLineUp"); - eqCharPos(cm.getCursor(), Pos(0, 2)); -}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"}); - -testCM("addLineClass", function(cm) { - function cls(line, text, bg, wrap, gutter) { - var i = cm.lineInfo(line); - eq(i.textClass, text); - eq(i.bgClass, bg); - eq(i.wrapClass, wrap); - if (typeof i.handle.gutterClass !== 'undefined') { - eq(i.handle.gutterClass, gutter); - } - } - cm.addLineClass(0, "text", "foo"); - cm.addLineClass(0, "text", "bar"); - cm.addLineClass(1, "background", "baz"); - cm.addLineClass(1, "wrap", "foo"); - cm.addLineClass(1, "gutter", "gutter-class"); - cls(0, "foo bar", null, null, null); - cls(1, null, "baz", "foo", "gutter-class"); - var lines = cm.display.lineDiv; - eq(byClassName(lines, "foo").length, 2); - eq(byClassName(lines, "bar").length, 1); - eq(byClassName(lines, "baz").length, 1); - eq(byClassName(lines, "gutter-class").length, 2); // Gutter classes are reflected in 2 nodes - cm.removeLineClass(0, "text", "foo"); - cls(0, "bar", null, null, null); - cm.removeLineClass(0, "text", "foo"); - cls(0, "bar", null, null, null); - cm.removeLineClass(0, "text", "bar"); - cls(0, null, null, null); - - cm.addLineClass(1, "wrap", "quux"); - cls(1, null, "baz", "foo quux", "gutter-class"); - cm.removeLineClass(1, "wrap"); - cls(1, null, "baz", null, "gutter-class"); - cm.removeLineClass(1, "gutter", "gutter-class"); - eq(byClassName(lines, "gutter-class").length, 0); - cls(1, null, "baz", null, null); - - cm.addLineClass(1, "gutter", "gutter-class"); - cls(1, null, "baz", null, "gutter-class"); - cm.removeLineClass(1, "gutter", "gutter-class"); - cls(1, null, "baz", null, null); - -}, {value: "hohoho\n", lineNumbers: true}); - -testCM("atomicMarker", function(cm) { - addDoc(cm, 10, 10); - - function atom(ll, cl, lr, cr, li, ri, ls, rs) { - var options = { - atomic: true, - inclusiveLeft: li, - inclusiveRight: ri - }; - - if (ls === true || ls === false) options["selectLeft"] = ls; - if (rs === true || rs === false) options["selectRight"] = rs; - - return cm.markText(Pos(ll, cl), Pos(lr, cr), options); - } - - // Can cursor to the left and right of a normal marker by jumping across it - var m = atom(0, 1, 0, 5); - cm.setCursor(Pos(0, 1)); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 5)); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - m.clear(); - - // Can't cursor to the left of a marker when inclusiveLeft=true - m = atom(0, 0, 0, 5, true); - eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 5)); - m.clear(); - - // Can't cursor to the left of a marker when inclusiveLeft=false and selectLeft=false - m = atom(0, 0, 0, 5, false, false, false); - cm.setCursor(Pos(0, 5)); - eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 5)); - m.clear(); - - // Can cursor to the left of a marker when inclusiveLeft=false and selectLeft=True - m = atom(0, 0, 0, 5, false, false, true); - cm.setCursor(Pos(0, 5)); - eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - m.clear(); - - // Can't cursor to the right of a marker when inclusiveRight=true - m = atom(0, 0, 0, 5, false, true); - cm.setCursor(Pos(0, 0)); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 6)); - m.clear(); - - // Can't cursor to the right of a marker when inclusiveRight=false and selectRight=false - m = atom(0, 0, 0, 5, false, false, true, false); - cm.setCursor(Pos(0, 0)); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 6)); - m.clear(); - - // Can cursor to the right of a marker when inclusiveRight=false and selectRight=True - m = atom(0, 0, 0, 5, false, false, true, true); - cm.setCursor(Pos(0, 0)); - eqCursorPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(0, 5)); - m.clear(); - - // Can't cursor to the right of a multiline marker when inclusiveRight=true - m = atom(8, 4, 9, 10, false, true); - cm.setCursor(Pos(9, 8)); - eqCursorPos(cm.getCursor(), Pos(8, 4), "set"); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(8, 4), "char right"); - cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(8, 4), "line down"); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(8, 3, "after")); - m.clear(); - - // Cursor jumps across a multiline atomic marker, - // and backspace deletes the entire marker - m = atom(1, 1, 3, 8); - cm.setCursor(Pos(0, 0)); - cm.setCursor(Pos(2, 0)); - eqCursorPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goCharRight"); - eqCursorPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("goLineUp"); - eqCursorPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineDown"); - eqCursorPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("delCharBefore"); - eq(cm.getValue().length, 80, "del chunk"); - m.clear(); - addDoc(cm, 10, 10); - - // Delete before an atomic marker deletes the entire marker - m = atom(3, 0, 5, 5); - cm.setCursor(Pos(3, 0)); - cm.execCommand("delWordAfter"); - eq(cm.getValue().length, 82, "del chunk"); - m.clear(); - addDoc(cm, 10, 10); -}); - -testCM("selectionBias", function(cm) { - cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true}); - cm.setCursor(Pos(0, 2)); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - cm.setCursor(Pos(0, 2)); - eqCursorPos(cm.getCursor(), Pos(0, 3)); - cm.setCursor(Pos(0, 2)); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - cm.setCursor(Pos(0, 2), null, {bias: -1}); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - cm.setCursor(Pos(0, 4)); - cm.setCursor(Pos(0, 2), null, {bias: 1}); - eqCursorPos(cm.getCursor(), Pos(0, 3)); -}, {value: "12345"}); - -testCM("selectionHomeEnd", function(cm) { - cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true}); - cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true}); - cm.setCursor(Pos(1, 2)); - cm.execCommand("goLineStart"); - eqCursorPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineEnd"); - eqCursorPos(cm.getCursor(), Pos(1, 3)); -}, {value: "ab\ncdef\ngh"}); - -testCM("readOnlyMarker", function(cm) { - function mark(ll, cl, lr, cr, at) { - return cm.markText(Pos(ll, cl), Pos(lr, cr), - {readOnly: true, atomic: at}); - } - var m = mark(0, 1, 0, 4); - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi", "end"); - eqCursorPos(cm.getCursor(), Pos(0, 2)); - eq(cm.getLine(0), "abcde"); - cm.execCommand("selectAll"); - cm.replaceSelection("oops", "around"); - eq(cm.getValue(), "oopsbcd"); - cm.undo(); - eqCursorPos(m.find().from, Pos(0, 1)); - eqCursorPos(m.find().to, Pos(0, 4)); - m.clear(); - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi", "around"); - eq(cm.getLine(0), "abhicde"); - eqCursorPos(cm.getCursor(), Pos(0, 4)); - m = mark(0, 2, 2, 2, true); - cm.setSelection(Pos(1, 1), Pos(2, 4)); - cm.replaceSelection("t", "end"); - eqCursorPos(cm.getCursor(), Pos(2, 3)); - eq(cm.getLine(2), "klto"); - cm.execCommand("goCharLeft"); - cm.execCommand("goCharLeft"); - eqCursorPos(cm.getCursor(), Pos(0, 2)); - cm.setSelection(Pos(0, 1), Pos(0, 3)); - cm.replaceSelection("xx", "around"); - eqCursorPos(cm.getCursor(), Pos(0, 3)); - eq(cm.getLine(0), "axxhicde"); -}, {value: "abcde\nfghij\nklmno\n"}); - -testCM("dirtyBit", function(cm) { - eq(cm.isClean(), true); - cm.replaceSelection("boo", null, "test"); - eq(cm.isClean(), false); - cm.undo(); - eq(cm.isClean(), true); - cm.replaceSelection("boo", null, "test"); - cm.replaceSelection("baz", null, "test"); - cm.undo(); - eq(cm.isClean(), false); - cm.markClean(); - eq(cm.isClean(), true); - cm.undo(); - eq(cm.isClean(), false); - cm.redo(); - eq(cm.isClean(), true); -}); - -testCM("changeGeneration", function(cm) { - cm.replaceSelection("x"); - var softGen = cm.changeGeneration(); - cm.replaceSelection("x"); - cm.undo(); - eq(cm.getValue(), ""); - is(!cm.isClean(softGen)); - cm.replaceSelection("x"); - var hardGen = cm.changeGeneration(true); - cm.replaceSelection("x"); - cm.undo(); - eq(cm.getValue(), "x"); - is(cm.isClean(hardGen)); -}); - -testCM("addKeyMap", function(cm) { - function sendKey(code) { - cm.triggerOnKeyDown({type: "keydown", keyCode: code, - preventDefault: function(){}, stopPropagation: function(){}}); - } - - sendKey(39); - eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); - var test = 0; - var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }} - cm.addKeyMap(map1); - sendKey(39); - eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); - eq(test, 1); - cm.addKeyMap(map2, true); - sendKey(39); - eq(test, 2); - cm.removeKeyMap(map1); - sendKey(39); - eq(test, 12); - cm.removeKeyMap(map2); - sendKey(39); - eq(test, 12); - eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); - cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); - sendKey(39); - eq(test, 55); - cm.removeKeyMap("mymap"); - sendKey(39); - eqCursorPos(cm.getCursor(), Pos(0, 3, "before")); -}, {value: "abc"}); - -function mouseDown(cm, button, pos, mods) { - var coords = cm.charCoords(pos, "window") - var event = {type: "mousedown", - preventDefault: Math.min, - which: button, - target: cm.display.lineDiv, - clientX: coords.left, clientY: coords.top} - if (mods) for (var prop in mods) event[prop] = mods[prop] - cm.triggerOnMouseDown(event) -} - -testCM("mouseBinding", function(cm) { - var fired = [] - cm.addKeyMap({ - "Shift-LeftClick": function(_cm, pos) { - eqCharPos(pos, Pos(1, 2)) - fired.push("a") - }, - "Shift-LeftDoubleClick": function() { fired.push("b") }, - "Shift-LeftTripleClick": function() { fired.push("c") } - }) - - function send(button, mods) { mouseDown(cm, button, Pos(1, 2), mods) } - send(1, {shiftKey: true}) - send(1, {shiftKey: true}) - send(1, {shiftKey: true}) - send(1, {}) - send(2, {ctrlKey: true}) - send(2, {ctrlKey: true}) - eq(fired.join(" "), "a b c") -}, {value: "foo\nbar\nbaz"}) - -testCM("configureMouse", function(cm) { - cm.setOption("configureMouse", function() { return {unit: "word"} }) - mouseDown(cm, 1, Pos(0, 5)) - eqCharPos(cm.getCursor("from"), Pos(0, 4)) - eqCharPos(cm.getCursor("to"), Pos(0, 7)) - cm.setOption("configureMouse", function() { return {extend: true} }) - mouseDown(cm, 1, Pos(0, 0)) - eqCharPos(cm.getCursor("from"), Pos(0, 0)) - eqCharPos(cm.getCursor("to"), Pos(0, 4)) -}, {value: "foo bar baz"}) - -testCM("findPosH", function(cm) { - forEach([{from: Pos(0, 0), to: Pos(0, 1, "before"), by: 1}, - {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true}, - {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"}, - {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"}, - {from: Pos(0, 0), to: Pos(2, 0, "after"), by: 20, unit: "word", hitSide: true}, - {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"}, - {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"}, - {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"}, - {from: Pos(1, 22), to: Pos(1, 5, "after"), by: -3, unit: "word"}, - {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5}, - {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"}, - {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true}, - {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true}, - {from: Pos(1, 15), to: Pos(2, 0, "after"), by: 50, hitSide: true}], function(t) { - var r = cm.findPosH(t.from, t.by, t.unit || "char"); - eqCursorPos(r, t.to); - eq(!!r.hitSide, !!t.hitSide); - }); -}, {value: "line one\nline two.something.other\n"}); - -testCM("beforeChange", function(cm) { - cm.on("beforeChange", function(cm, change) { - var text = []; - for (var i = 0; i < change.text.length; ++i) - text.push(change.text[i].replace(/\s/g, "_")); - change.update(null, null, text); - }); - cm.setValue("hello, i am a\nnew document\n"); - eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); - CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) { - if (change.from.line == 0) change.cancel(); - }); - cm.setValue("oops"); // Canceled - eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); - cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0)); - eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); -}, {value: "abcdefghijk"}); - -testCM("beforeChangeUndo", function(cm) { - cm.replaceRange("hi", Pos(0, 0), Pos(0)); - cm.replaceRange("bye", Pos(0, 0), Pos(0)); - eq(cm.historySize().undo, 2); - cm.on("beforeChange", function(cm, change) { - is(!change.update); - change.cancel(); - }); - cm.undo(); - eq(cm.historySize().undo, 0); - eq(cm.getValue(), "bye\ntwo"); -}, {value: "one\ntwo"}); - -testCM("beforeSelectionChange", function(cm) { - function notAtEnd(cm, pos) { - var len = cm.getLine(pos.line).length; - if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1); - return pos; - } - cm.on("beforeSelectionChange", function(cm, obj) { - obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor), - head: notAtEnd(cm, obj.ranges[0].head)}]); - }); - - addDoc(cm, 10, 10); - cm.execCommand("goLineEnd"); - eqCursorPos(cm.getCursor(), Pos(0, 9)); - cm.execCommand("selectAll"); - eqCursorPos(cm.getCursor("start"), Pos(0, 0)); - eqCursorPos(cm.getCursor("end"), Pos(9, 9)); -}); - -testCM("change_removedText", function(cm) { - cm.setValue("abc\ndef"); - - var removedText = []; - cm.on("change", function(cm, change) { - removedText.push(change.removed); - }); - - cm.operation(function() { - cm.replaceRange("xyz", Pos(0, 0), Pos(1,1)); - cm.replaceRange("123", Pos(0,0)); - }); - - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "abc\nd"); - eq(removedText[1].join("\n"), ""); - - var removedText = []; - cm.undo(); - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "123"); - eq(removedText[1].join("\n"), "xyz"); - - var removedText = []; - cm.redo(); - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "abc\nd"); - eq(removedText[1].join("\n"), ""); -}); - -testCM("lineStyleFromMode", function(cm) { - CodeMirror.defineMode("test_mode", function() { - return {token: function(stream) { - if (stream.match(/^\[[^\]]*\]/)) return " line-brackets "; - if (stream.match(/^\([^\)]*\)/)) return " line-background-parens "; - if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg "; - stream.match(/^\s+|^\S+/); - }}; - }); - cm.setOption("mode", "test_mode"); - var bracketElts = byClassName(cm.getWrapperElement(), "brackets"); - eq(bracketElts.length, 1, "brackets count"); - eq(bracketElts[0].nodeName, "PRE"); - is(!/brackets.*brackets/.test(bracketElts[0].className)); - var parenElts = byClassName(cm.getWrapperElement(), "parens"); - eq(parenElts.length, 1, "parens count"); - eq(parenElts[0].nodeName, "DIV"); - is(!/parens.*parens/.test(parenElts[0].className)); - eq(parenElts[0].parentElement.nodeName, "DIV"); - - is(byClassName(cm.getWrapperElement(), "bg").length > 0); - is(byClassName(cm.getWrapperElement(), "line").length > 0); - var spanElts = byClassName(cm.getWrapperElement(), "cm-span"); - eq(spanElts.length, 2); - is(/^\s*cm-span\s*$/.test(spanElts[0].className)); -}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: "}); - -testCM("lineStyleFromBlankLine", function(cm) { - CodeMirror.defineMode("lineStyleFromBlankLine_mode", function() { - return {token: function(stream) { stream.skipToEnd(); return "comment"; }, - blankLine: function() { return "line-blank"; }}; - }); - cm.setOption("mode", "lineStyleFromBlankLine_mode"); - var blankElts = byClassName(cm.getWrapperElement(), "blank"); - eq(blankElts.length, 1); - eq(blankElts[0].nodeName, "PRE"); - cm.replaceRange("x", Pos(1, 0)); - blankElts = byClassName(cm.getWrapperElement(), "blank"); - eq(blankElts.length, 0); -}, {value: "foo\n\nbar"}); - -CodeMirror.registerHelper("xxx", "a", "A"); -CodeMirror.registerHelper("xxx", "b", "B"); -CodeMirror.defineMode("yyy", function() { - return { - token: function(stream) { stream.skipToEnd(); }, - xxx: ["a", "b", "q"] - }; -}); -CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C"); - -testCM("helpers", function(cm) { - cm.setOption("mode", "yyy"); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B"); - cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}}); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C"); - cm.setOption("mode", "javascript"); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); -}); - -testCM("selectionHistory", function(cm) { - for (var i = 0; i < 3; i++) { - cm.setExtending(true); - cm.execCommand("goCharRight"); - cm.setExtending(false); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - } - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "c"); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), ""); - eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "b"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), ""); - eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), "c"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), ""); - eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); -}, {value: "a b c d"}); - -testCM("selectionChangeReducesRedo", function(cm) { - cm.replaceSelection("X"); - cm.execCommand("goCharRight"); - cm.undoSelection(); - cm.execCommand("selectAll"); - cm.undoSelection(); - eq(cm.getValue(), "Xabc"); - eqCursorPos(cm.getCursor(), Pos(0, 1)); - cm.undoSelection(); - eq(cm.getValue(), "abc"); -}, {value: "abc"}); - -testCM("selectionHistoryNonOverlapping", function(cm) { - cm.setSelection(Pos(0, 0), Pos(0, 1)); - cm.setSelection(Pos(0, 2), Pos(0, 3)); - cm.execCommand("undoSelection"); - eqCursorPos(cm.getCursor("anchor"), Pos(0, 0)); - eqCursorPos(cm.getCursor("head"), Pos(0, 1)); -}, {value: "1234"}); - -testCM("cursorMotionSplitsHistory", function(cm) { - cm.replaceSelection("a"); - cm.execCommand("goCharRight"); - cm.replaceSelection("b"); - cm.replaceSelection("c"); - cm.undo(); - eq(cm.getValue(), "a1234"); - eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); - cm.undo(); - eq(cm.getValue(), "1234"); - eqCursorPos(cm.getCursor(), Pos(0, 0)); -}, {value: "1234"}); - -testCM("selChangeInOperationDoesNotSplit", function(cm) { - for (var i = 0; i < 4; i++) { - cm.operation(function() { - cm.replaceSelection("x"); - cm.setCursor(Pos(0, cm.getCursor().ch - 1)); - }); - } - eqCursorPos(cm.getCursor(), Pos(0, 0)); - eq(cm.getValue(), "xxxxa"); - cm.undo(); - eq(cm.getValue(), "a"); -}, {value: "a"}); - -testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) { - cm.replaceSelection("U", null, "foo"); - cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"}); - cm.undoSelection(); - eq(cm.getValue(), "a"); - cm.replaceSelection("V", null, "foo"); - cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"}); - cm.undoSelection(); - eq(cm.getValue(), "Va"); -}, {value: "a"}); - -testCM("getTokenAt", function(cm) { - var tokPlus = cm.getTokenAt(Pos(0, 2)); - eq(tokPlus.type, "operator"); - eq(tokPlus.string, "+"); - var toks = cm.getLineTokens(0); - eq(toks.length, 3); - forEach([["number", "1"], ["operator", "+"], ["number", "2"]], function(expect, i) { - eq(toks[i].type, expect[0]); - eq(toks[i].string, expect[1]); - }); -}, {value: "1+2", mode: "javascript"}); - -testCM("getTokenTypeAt", function(cm) { - eq(cm.getTokenTypeAt(Pos(0, 0)), "number"); - eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); - cm.addOverlay({ - token: function(stream) { - if (stream.match("foo")) return "foo"; - else stream.next(); - } - }); - eq(byClassName(cm.getWrapperElement(), "cm-foo").length, 1); - eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); -}, {value: "1 + 'foo'", mode: "javascript"}); - -testCM("addOverlay", function(cm) { - cm.addOverlay({ - token: function(stream) { - var base = stream.baseToken() - if (!/comment/.test(base.type) && stream.match(/\d+/)) return "x" - stream.next() - } - }) - var x = byClassName(cm.getWrapperElement(), "cm-x") - is(x.length, 1) - is(x[0].textContent, "233") - cm.replaceRange("", Pos(0, 4), Pos(0, 6)) - is(byClassName(cm.getWrapperElement(), "cm-x").length, 2) -}, {value: "foo /* 100 */\nbar + 233;\nbaz", mode: "javascript"}) - -testCM("resizeLineWidget", function(cm) { - addDoc(cm, 200, 3); - var widget = document.createElement("pre"); - widget.innerHTML = "imwidget"; - widget.style.background = "yellow"; - cm.addLineWidget(1, widget, {noHScroll: true}); - cm.setSize(40); - is(widget.parentNode.offsetWidth < 42); -}); - -testCM("combinedOperations", function(cm) { - var place = document.getElementById("testground"); - var other = CodeMirror(place, {value: "123"}); - try { - cm.operation(function() { - cm.addLineClass(0, "wrap", "foo"); - other.addLineClass(0, "wrap", "foo"); - }); - eq(byClassName(cm.getWrapperElement(), "foo").length, 1); - eq(byClassName(other.getWrapperElement(), "foo").length, 1); - cm.operation(function() { - cm.removeLineClass(0, "wrap", "foo"); - other.removeLineClass(0, "wrap", "foo"); - }); - eq(byClassName(cm.getWrapperElement(), "foo").length, 0); - eq(byClassName(other.getWrapperElement(), "foo").length, 0); - } finally { - place.removeChild(other.getWrapperElement()); - } -}, {value: "abc"}); - -testCM("eventOrder", function(cm) { - var seen = []; - cm.on("change", function() { - if (!seen.length) cm.replaceSelection("."); - seen.push("change"); - }); - cm.on("cursorActivity", function() { - cm.replaceSelection("!"); - seen.push("activity"); - }); - cm.replaceSelection("/"); - eq(seen.join(","), "change,change,activity,change"); -}); - -testCM("splitSpaces_nonspecial", function(cm) { - eq(byClassName(cm.getWrapperElement(), "cm-invalidchar").length, 0); -}, { - specialChars: /[\u00a0]/, - value: "spaces -> <- between" -}); - -test("core_rmClass", function() { - var node = document.createElement("div"); - node.className = "foo-bar baz-quux yadda"; - CodeMirror.rmClass(node, "quux"); - eq(node.className, "foo-bar baz-quux yadda"); - CodeMirror.rmClass(node, "baz-quux"); - eq(node.className, "foo-bar yadda"); - CodeMirror.rmClass(node, "yadda"); - eq(node.className, "foo-bar"); - CodeMirror.rmClass(node, "foo-bar"); - eq(node.className, ""); - node.className = " foo "; - CodeMirror.rmClass(node, "foo"); - eq(node.className, ""); -}); - -test("core_addClass", function() { - var node = document.createElement("div"); - CodeMirror.addClass(node, "a"); - eq(node.className, "a"); - CodeMirror.addClass(node, "a"); - eq(node.className, "a"); - CodeMirror.addClass(node, "b"); - eq(node.className, "a b"); - CodeMirror.addClass(node, "a"); - CodeMirror.addClass(node, "b"); - eq(node.className, "a b"); -}); - -testCM("lineSeparator", function(cm) { - eq(cm.lineCount(), 3); - eq(cm.getLine(1), "bar\r"); - eq(cm.getLine(2), "baz\rquux"); - cm.setOption("lineSeparator", "\r"); - eq(cm.lineCount(), 5); - eq(cm.getLine(4), "quux"); - eq(cm.getValue(), "foo\rbar\r\rbaz\rquux"); - eq(cm.getValue("\n"), "foo\nbar\n\nbaz\nquux"); - cm.setOption("lineSeparator", null); - cm.setValue("foo\nbar\r\nbaz\rquux"); - eq(cm.lineCount(), 4); -}, {value: "foo\nbar\r\nbaz\rquux", - lineSeparator: "\n"}); - -var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ -var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res } -var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res } - -function makeItWrapAfter(cm, pos) { - var firstLineTop = cm.cursorCoords(Pos(0, 0)).top; - for(var w = 0, posTop; posTop != firstLineTop; ++w) { - cm.setSize(w); - posTop = cm.charCoords(pos).top; - } -} - -function countIf(arr, f) { - var result = 0 - for (var i = 0; i < arr.length; i++) if (f[arr[i]]) result++ - return result -} - -function testMoveBidi(str) { - testCM("move_bidi_" + str, function(cm) { - if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return; - cm.getScrollerElement().style.fontFamily = "monospace"; - makeItWrapAfter(cm, Pos(0, 5)); - - var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) }); - var lineBreaks = {} - lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w'; - if (str.indexOf("\n") != -1) { - lineBreaks[steps - 2] = 'n'; - } - - // Make sure we are at the visual beginning of the first line - cm.execCommand("goLineStart"); - - var prevCoords = cm.cursorCoords(), coords; - for(var i = 0; i < steps; ++i) { - cm.execCommand("goCharRight"); - coords = cm.cursorCoords(); - if ((i >= 10 && i <= 12) && !lineBreaks[i] && coords.left < prevCoords.left && coords.top > prevCoords.top) { - // The first line wraps twice - lineBreaks[i] = 'w'; - } - if (!lineBreaks[i]) { - is(coords.left > prevCoords.left, "In step " + i + ", cursor didn't move right"); - eq(coords.top, prevCoords.top, "In step " + i + ", cursor moved out of line"); - } else { - is(coords.left < prevCoords.left, i); - is(coords.top > prevCoords.top, i); - } - prevCoords = coords; - } - - cm.execCommand("goCharRight"); - coords = cm.cursorCoords(); - eq(coords.left, prevCoords.left, "Moving " + steps + " steps right didn't reach the end"); - eq(coords.top, prevCoords.top, "Moving " + steps + " steps right didn't reach the end"); - - for(i = steps - 1; i >= 0; --i) { - cm.execCommand("goCharLeft"); - coords = cm.cursorCoords(); - if (!(lineBreaks[i] == 'n' || lineBreaks[i + 1] == 'w')) { - is(coords.left < prevCoords.left, "In step " + i + ", cursor didn't move left"); - eq(coords.top, prevCoords.top, "In step " + i + ", cursor is not at the same line anymore"); - } else { - is(coords.left > prevCoords.left, i); - is(coords.top < prevCoords.top, i); - } - prevCoords = coords; - } - - cm.execCommand("goCharLeft"); - coords = cm.cursorCoords(); - eq(coords.left, prevCoords.left, "Moving " + steps + " steps left didn't reach the beginning"); - eq(coords.top, prevCoords.top, "Moving " + steps + " steps left didn't reach the beginning"); - }, {value: str, lineWrapping: true}) -}; - -function testMoveEndBidi(str) { - testCM("move_end_bidi_" + str, function(cm) { - cm.getScrollerElement().style.fontFamily = "monospace"; - makeItWrapAfter(cm, Pos(0, 5)); - - cm.execCommand("goLineStart"); - var pos = cm.doc.getCursor(); - cm.execCommand("goCharLeft"); - eqCursorPos(pos, cm.doc.getCursor()); - - cm.execCommand("goLineEnd"); - pos = cm.doc.getCursor(); - cm.execCommand("goColumnRight"); - eqCursorPos(pos, cm.doc.getCursor()); - }, {value: str, lineWrapping: true}) -}; - -var bidiTests = []; - -// We don't correctly implement L1 UBA -// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331501 -// and https://bugs.chromium.org/p/chromium/issues/detail?id=673405 -/* -bidiTests.push("Say ا ب جabj\nS"); -bidiTests.push("Sayyy ا ا ب ج"); -*/ - -bidiTests.push("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); -if (!window.automatedTests) bidiTests.push("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); -bidiTests.push("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); -bidiTests.push("؅؁ĆՕƿɁǞϮؠȩóć\nď"); -bidiTests.push("RŨďңŪzϢŎƏԖڇڦ\nӈ"); -bidiTests.push("ό׊۷٢ԜһОצЉيčǟ\nѩ"); -bidiTests.push("ۑÚҳҕڬġڹհяųKV\nr"); -bidiTests.push("źڻғúہ4ם1Ƞc1a\nԁ"); -bidiTests.push("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); -bidiTests.push("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); -bidiTests.push("۹ؼL۵ĺȧКԙػא7״\nم"); -bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior - -bidiTests.push("քմѧǮßپüŢҍҞўڳ\nӧ"); - -//bidiTests.push("Count ١ ٢ ٣ ٤"); -//bidiTests.push("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); -//bidiTests.push("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); -//bidiTests.push("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); -//bidiTests.push("aѴNijȻهˇ҃ڱӧǻֵ\na"); -//bidiTests.push(" a٧ا٢ ب جa\nS"); - -for (var i = 0; i < bidiTests.length; ++i) { - testMoveBidi(bidiTests[i]); - testMoveEndBidi(bidiTests[i]); -} - -/* -for (var i = 0; i < 5; ++i) { - testMoveBidi(getString(12) + "\n" + getString(1)); -} -*/ - -function testCoordsWrappedBidi(str) { - testCM("coords_wrapped_bidi_" + str, function(cm) { - cm.getScrollerElement().style.fontFamily = "monospace"; - makeItWrapAfter(cm, Pos(0, 5)); - - // Make sure we are at the visual beginning of the first line - var pos = Pos(0, 0), lastPos; - cm.doc.setCursor(pos); - do { - lastPos = pos; - cm.execCommand("goCharLeft"); - pos = cm.doc.getCursor(); - } while (pos != lastPos) - - var top = cm.charCoords(Pos(0, 0)).top, lastTop; - for (var i = 1; i < str.length; ++i) { - lastTop = top; - top = cm.charCoords(Pos(0, i)).top; - is(top >= lastTop); - } - }, {value: str, lineWrapping: true}) -}; - -testCoordsWrappedBidi("Count ١ ٢ ٣ ٤"); -/* -for (var i = 0; i < 5; ++i) { - testCoordsWrappedBidi(getString(50)); -} -*/ - -testCM("rtl_wrapped_selection", function(cm) { - cm.setSelection(Pos(0, 10), Pos(0, 190)) - is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3) -}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true}) - -testCM("bidi_wrapped_selection", function(cm) { - cm.setSize(cm.charCoords(Pos(0, 10), "editor").left) - cm.setSelection(Pos(0, 37), Pos(0, 80)) - var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected") - is(blocks.length >= 2) - is(blocks.length <= 3) - var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect() - is(boxTop.left > cm.charCoords(Pos(0, 1)).right) - is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left) -}, {value: "

مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت

", lineWrapping: true}) - -testCM("delete_wrapped", function(cm) { - makeItWrapAfter(cm, Pos(0, 2)); - cm.doc.setCursor(Pos(0, 3, "after")); - cm.deleteH(-1, "char"); - eq(cm.getLine(0), "1245"); -}, {value: "12345", lineWrapping: true}) - -testCM("issue_4878", function(cm) { - if (window.automatedTests) return - cm.setCursor(Pos(1, 12, "after")); - cm.moveH(-1, "char"); - eqCursorPos(cm.getCursor(), Pos(0, 113, "before")); -}, {value: " في تطبيق السمات مرة واحدة https://github.com/codemirror/CodeMirror/issues/4878#issuecomment-330550964على سبيل المثال \"foo bar\"\n" + -" سيتم تعيين", direction: "rtl", lineWrapping: true}); - -CodeMirror.defineMode("lookahead_mode", function() { - // Colors text as atom if the line two lines down has an x in it - return { - token: function(stream) { - stream.skipToEnd() - return /x/.test(stream.lookAhead(2)) ? "atom" : null - } - } -}) - -testCM("mode_lookahead", function(cm) { - eq(cm.getTokenAt(Pos(0, 1)).type, "atom") - eq(cm.getTokenAt(Pos(1, 1)).type, "atom") - eq(cm.getTokenAt(Pos(2, 1)).type, null) - cm.replaceRange("\n", Pos(2, 0)) - eq(cm.getTokenAt(Pos(0, 1)).type, null) - eq(cm.getTokenAt(Pos(1, 1)).type, "atom") -}, {value: "foo\na\nx\nx\n", mode: "lookahead_mode"}) diff --git a/CODE/js/codemirror/test/vim_test.js b/CODE/js/codemirror/test/vim_test.js deleted file mode 100644 index c75a1646..00000000 --- a/CODE/js/codemirror/test/vim_test.js +++ /dev/null @@ -1,5079 +0,0 @@ -var Pos = CodeMirror.Pos; -CodeMirror.Vim.suppressErrorLogging = true; - -var code = '' + -' wOrd1 (#%\n' + -' word3] \n' + -'aopop pop 0 1 2 3 4\n' + -' (a) [b] {c} \n' + -'int getchar(void) {\n' + -' static char buf[BUFSIZ];\n' + -' static char *bufp = buf;\n' + -' if (n == 0) { /* buffer is empty */\n' + -' n = read(0, buf, sizeof buf);\n' + -' bufp = buf;\n' + -' }\n' + -'\n' + -' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' + -' \n' + -'}\n'; - -var lines = (function() { - lineText = code.split('\n'); - var ret = []; - for (var i = 0; i < lineText.length; i++) { - ret[i] = { - line: i, - length: lineText[i].length, - lineText: lineText[i], - textStart: /^\s*/.exec(lineText[i])[0].length - }; - } - return ret; -})(); -var endOfDocument = makeCursor(lines.length - 1, - lines[lines.length - 1].length); -var wordLine = lines[0]; -var bigWordLine = lines[1]; -var charLine = lines[2]; -var bracesLine = lines[3]; -var seekBraceLine = lines[4]; -var foldingStart = lines[7]; -var foldingEnd = lines[11]; - -var word1 = { - start: new Pos(wordLine.line, 1), - end: new Pos(wordLine.line, 5) -}; -var word2 = { - start: new Pos(wordLine.line, word1.end.ch + 2), - end: new Pos(wordLine.line, word1.end.ch + 4) -}; -var word3 = { - start: new Pos(bigWordLine.line, 1), - end: new Pos(bigWordLine.line, 5) -}; -var bigWord1 = word1; -var bigWord2 = word2; -var bigWord3 = { - start: new Pos(bigWordLine.line, 1), - end: new Pos(bigWordLine.line, 7) -}; -var bigWord4 = { - start: new Pos(bigWordLine.line, bigWord1.end.ch + 3), - end: new Pos(bigWordLine.line, bigWord1.end.ch + 7) -}; - -var oChars = [ new Pos(charLine.line, 1), - new Pos(charLine.line, 3), - new Pos(charLine.line, 7) ]; -var pChars = [ new Pos(charLine.line, 2), - new Pos(charLine.line, 4), - new Pos(charLine.line, 6), - new Pos(charLine.line, 8) ]; -var numChars = [ new Pos(charLine.line, 10), - new Pos(charLine.line, 12), - new Pos(charLine.line, 14), - new Pos(charLine.line, 16), - new Pos(charLine.line, 18)]; -var parens1 = { - start: new Pos(bracesLine.line, 1), - end: new Pos(bracesLine.line, 3) -}; -var squares1 = { - start: new Pos(bracesLine.line, 5), - end: new Pos(bracesLine.line, 7) -}; -var curlys1 = { - start: new Pos(bracesLine.line, 9), - end: new Pos(bracesLine.line, 11) -}; -var seekOutside = { - start: new Pos(seekBraceLine.line, 1), - end: new Pos(seekBraceLine.line, 16) -}; -var seekInside = { - start: new Pos(seekBraceLine.line, 14), - end: new Pos(seekBraceLine.line, 11) -}; -var foldingRangeDown = { - start: new Pos(foldingStart.line, 3), - end: new Pos(foldingEnd.line, 0) -}; -var foldingRangeUp = { - start: new Pos(foldingEnd.line, 0), - end: new Pos(foldingStart.line, 0) -}; - -function copyCursor(cur) { - return new Pos(cur.line, cur.ch); -} - -function forEach(arr, func) { - for (var i = 0; i < arr.length; i++) { - func(arr[i], i, arr); - } -} - -function expectFail(fn) { - try { - fn(); - } catch(expected) { - return; - }; - throw new Error("Expected to throw an error"); -} - -function testVim(name, run, opts, expectedFail) { - var vimOpts = { - lineNumbers: true, - vimMode: true, - showCursorWhenSelecting: true, - value: code - }; - for (var prop in opts) { - if (opts.hasOwnProperty(prop)) { - vimOpts[prop] = opts[prop]; - } - } - return test('vim_' + name, function() { - var place = document.getElementById("testground"); - var cm = CodeMirror(place, vimOpts); - var vim = CodeMirror.Vim.maybeInitVimState_(cm); - - function doKeysFn(cm) { - return function(args) { - if (args instanceof Array) { - arguments = args; - } - for (var i = 0; i < arguments.length; i++) { - var result = CodeMirror.Vim.handleKey(cm, arguments[i]); - if (!result && cm.state.vim.insertMode) { - cm.replaceSelections(fillArray(arguments[i], cm.listSelections().length)); - } - } - } - } - function doInsertModeKeysFn(cm) { - return function(args) { - if (args instanceof Array) { arguments = args; } - function executeHandler(handler) { - if (typeof handler == 'string') { - CodeMirror.commands[handler](cm); - } else { - handler(cm); - } - return true; - } - for (var i = 0; i < arguments.length; i++) { - var key = arguments[i]; - // Find key in keymap and handle. - var handled = CodeMirror.lookupKey(key, cm.getOption('keyMap'), executeHandler, cm); - // Record for insert mode. - if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') { - var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges; - if (lastChange && (key.indexOf('Delete') != -1 || key.indexOf('Backspace') != -1)) { - lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key)); - } - } - } - } - } - function doExFn(cm) { - return function(command) { - cm.openDialog = helpers.fakeOpenDialog(command); - helpers.doKeys(':'); - } - } - function assertCursorAtFn(cm) { - return function(line, ch) { - var pos; - if (ch == null && typeof line.line == 'number') { - pos = line; - } else { - pos = makeCursor(line, ch); - } - eqCursorPos(cm.getCursor(), pos); - } - } - function fakeOpenDialog(result) { - return function(text, callback) { - return callback(result); - } - } - function fakeOpenNotification(matcher) { - return function(text) { - matcher(text); - } - } - var helpers = { - doKeys: doKeysFn(cm), - // Warning: Only emulates keymap events, not character insertions. Use - // replaceRange to simulate character insertions. - // Keys are in CodeMirror format, NOT vim format. - doInsertModeKeys: doInsertModeKeysFn(cm), - doEx: doExFn(cm), - assertCursorAt: assertCursorAtFn(cm), - fakeOpenDialog: fakeOpenDialog, - fakeOpenNotification: fakeOpenNotification, - getRegisterController: function() { - return CodeMirror.Vim.getRegisterController(); - } - } - CodeMirror.Vim.resetVimGlobalState_(); - var successful = false; - var savedOpenNotification = cm.openNotification; - var savedOpenDialog = cm.openDialog; - try { - run(cm, vim, helpers); - successful = true; - } finally { - cm.openNotification = savedOpenNotification; - cm.openDialog = savedOpenDialog; - if (!successful || verbose) { - place.style.visibility = "visible"; - } else { - place.removeChild(cm.getWrapperElement()); - } - } - }, expectedFail); -}; -testVim('qq@q', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'q', 'l', 'l', 'q'); - helpers.assertCursorAt(0,2); - helpers.doKeys('@', 'q'); - helpers.assertCursorAt(0,4); -}, { value: ' '}); -testVim('@@', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'q', 'l', 'l', 'q'); - helpers.assertCursorAt(0,2); - helpers.doKeys('@', 'q'); - helpers.assertCursorAt(0,4); - helpers.doKeys('@', '@'); - helpers.assertCursorAt(0,6); -}, { value: ' '}); -var jumplistScene = ''+ - 'word\n'+ - '(word)\n'+ - '{word\n'+ - 'word.\n'+ - '\n'+ - 'word search\n'+ - '}word\n'+ - 'word\n'+ - 'word\n'; -function testJumplist(name, keys, endPos, startPos, dialog) { - endPos = makeCursor(endPos[0], endPos[1]); - startPos = makeCursor(startPos[0], startPos[1]); - testVim(name, function(cm, vim, helpers) { - CodeMirror.Vim.resetVimGlobalState_(); - if(dialog)cm.openDialog = helpers.fakeOpenDialog('word'); - cm.setCursor(startPos); - helpers.doKeys.apply(null, keys); - helpers.assertCursorAt(endPos); - }, {value: jumplistScene}); -} -testJumplist('jumplist_H', ['H', ''], [5,2], [5,2]); -testJumplist('jumplist_M', ['M', ''], [2,2], [2,2]); -testJumplist('jumplist_L', ['L', ''], [2,2], [2,2]); -testJumplist('jumplist_[[', ['[', '[', ''], [5,2], [5,2]); -testJumplist('jumplist_]]', [']', ']', ''], [2,2], [2,2]); -testJumplist('jumplist_G', ['G', ''], [5,2], [5,2]); -testJumplist('jumplist_gg', ['g', 'g', ''], [5,2], [5,2]); -testJumplist('jumplist_%', ['%', ''], [1,5], [1,5]); -testJumplist('jumplist_{', ['{', ''], [1,5], [1,5]); -testJumplist('jumplist_}', ['}', ''], [1,5], [1,5]); -testJumplist('jumplist_\'', ['m', 'a', 'h', '\'', 'a', 'h', ''], [1,0], [1,5]); -testJumplist('jumplist_`', ['m', 'a', 'h', '`', 'a', 'h', ''], [1,5], [1,5]); -testJumplist('jumplist_*_cachedCursor', ['*', ''], [1,3], [1,3]); -testJumplist('jumplist_#_cachedCursor', ['#', ''], [1,3], [1,3]); -testJumplist('jumplist_n', ['#', 'n', ''], [1,1], [2,3]); -testJumplist('jumplist_N', ['#', 'N', ''], [1,1], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', ''], [2,3], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', '', '2', ''], [5,0], [2,3]); -testJumplist('jumplist_repeated_motion', ['3', '*', ''], [2,3], [2,3]); -testJumplist('jumplist_/', ['/', ''], [2,3], [2,3], 'dialog'); -testJumplist('jumplist_?', ['?', ''], [2,3], [2,3], 'dialog'); -testJumplist('jumplist_skip_deleted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], - [0,2], [0,2]); -testJumplist('jumplist_skip_deleted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], - [1,0], [0,2]); - -/** - * @param name Name of the test - * @param keys An array of keys or a string with a single key to simulate. - * @param endPos The expected end position of the cursor. - * @param startPos The position the cursor should start at, defaults to 0, 0. - */ -function testMotion(name, keys, endPos, startPos) { - testVim(name, function(cm, vim, helpers) { - if (!startPos) { - startPos = new Pos(0, 0); - } - cm.setCursor(startPos); - helpers.doKeys(keys); - helpers.assertCursorAt(endPos); - }); -} - -function testMotionWithFolding(name, keys, endPos, startPos) { - testVim(name, function (cm, vim, helpers) { - cm.foldCode(startPos); - cm.foldCode(endPos); - cm.setCursor(startPos); - helpers.doKeys(keys); - helpers.assertCursorAt(endPos) - }) -} - -function makeCursor(line, ch) { - return new Pos(line, ch); -} - -function offsetCursor(cur, offsetLine, offsetCh) { - return new Pos(cur.line + offsetLine, cur.ch + offsetCh); -} - -// Motion tests -testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4)); -testMotion('|_repeat', ['3', '|'], makeCursor(0, 2), makeCursor(0,4)); -testMotion('h', 'h', makeCursor(0, 0), word1.start); -testMotion('h_repeat', ['3', 'h'], offsetCursor(word1.end, 0, -3), word1.end); -testMotion('l', 'l', makeCursor(0, 1)); -testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2)); -testMotion('j', 'j', offsetCursor(word1.end, 1, 0), word1.end); -testMotion('j_repeat', ['2', 'j'], offsetCursor(word1.end, 2, 0), word1.end); -testMotion('j_repeat_clip', ['1000', 'j'], endOfDocument); -testMotion('k', 'k', offsetCursor(word3.end, -1, 0), word3.end); -testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4)); -testMotion('k_repeat_clip', ['1000', 'k'], makeCursor(0, 4), makeCursor(2, 4)); -testMotion('w', 'w', word1.start); -testMotion('keepHPos', ['5', 'j', 'j', '7', 'k'], makeCursor(8, 12), makeCursor(12, 12)); -testMotion('keepHPosEol', ['$', '2', 'j'], makeCursor(2, 18)); -testMotion('w_multiple_newlines_no_space', 'w', makeCursor(12, 2), makeCursor(11, 2)); -testMotion('w_multiple_newlines_with_space', 'w', makeCursor(14, 0), makeCursor(12, 51)); -testMotion('w_repeat', ['2', 'w'], word2.start); -testMotion('w_wrap', ['w'], word3.start, word2.start); -testMotion('w_endOfDocument', 'w', endOfDocument, endOfDocument); -testMotion('w_start_to_end', ['1000', 'w'], endOfDocument, makeCursor(0, 0)); -testMotion('W', 'W', bigWord1.start); -testMotion('W_repeat', ['2', 'W'], bigWord3.start, bigWord1.start); -testMotion('e', 'e', word1.end); -testMotion('e_repeat', ['2', 'e'], word2.end); -testMotion('e_wrap', 'e', word3.end, word2.end); -testMotion('e_endOfDocument', 'e', endOfDocument, endOfDocument); -testMotion('e_start_to_end', ['1000', 'e'], endOfDocument, makeCursor(0, 0)); -testMotion('b', 'b', word3.start, word3.end); -testMotion('b_repeat', ['2', 'b'], word2.start, word3.end); -testMotion('b_wrap', 'b', word2.start, word3.start); -testMotion('b_startOfDocument', 'b', makeCursor(0, 0), makeCursor(0, 0)); -testMotion('b_end_to_start', ['1000', 'b'], makeCursor(0, 0), endOfDocument); -testMotion('ge', ['g', 'e'], word2.end, word3.end); -testMotion('ge_repeat', ['2', 'g', 'e'], word1.end, word3.start); -testMotion('ge_wrap', ['g', 'e'], word2.end, word3.start); -testMotion('ge_startOfDocument', ['g', 'e'], makeCursor(0, 0), - makeCursor(0, 0)); -testMotion('ge_end_to_start', ['1000', 'g', 'e'], makeCursor(0, 0), endOfDocument); -testMotion('gg', ['g', 'g'], makeCursor(lines[0].line, lines[0].textStart), - makeCursor(3, 1)); -testMotion('gg_repeat', ['3', 'g', 'g'], - makeCursor(lines[2].line, lines[2].textStart)); -testMotion('G', 'G', - makeCursor(lines[lines.length - 1].line, lines[lines.length - 1].textStart), - makeCursor(3, 1)); -testMotion('G_repeat', ['3', 'G'], makeCursor(lines[2].line, - lines[2].textStart)); -// TODO: Make the test code long enough to test Ctrl-F and Ctrl-B. -testMotion('0', '0', makeCursor(0, 0), makeCursor(0, 8)); -testMotion('^', '^', makeCursor(0, lines[0].textStart), makeCursor(0, 8)); -testMotion('+', '+', makeCursor(1, lines[1].textStart), makeCursor(0, 8)); -testMotion('-', '-', makeCursor(0, lines[0].textStart), makeCursor(1, 4)); -testMotion('_', ['6','_'], makeCursor(5, lines[5].textStart), makeCursor(0, 8)); -testMotion('$', '$', makeCursor(0, lines[0].length - 1), makeCursor(0, 1)); -testMotion('$_repeat', ['2', '$'], makeCursor(1, lines[1].length - 1), - makeCursor(0, 3)); -testMotion('$', ['v', '$'], makeCursor(0, lines[0].length), makeCursor(0, 1)); -testMotion('f', ['f', 'p'], pChars[0], makeCursor(charLine.line, 0)); -testMotion('f_repeat', ['2', 'f', 'p'], pChars[2], pChars[0]); -testMotion('f_num', ['f', '2'], numChars[2], makeCursor(charLine.line, 0)); -testMotion('t', ['t','p'], offsetCursor(pChars[0], 0, -1), - makeCursor(charLine.line, 0)); -testMotion('t_repeat', ['2', 't', 'p'], offsetCursor(pChars[2], 0, -1), - pChars[0]); -testMotion('F', ['F', 'p'], pChars[0], pChars[1]); -testMotion('F_repeat', ['2', 'F', 'p'], pChars[0], pChars[2]); -testMotion('T', ['T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[1]); -testMotion('T_repeat', ['2', 'T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[2]); -testMotion('%_parens', ['%'], parens1.end, parens1.start); -testMotion('%_squares', ['%'], squares1.end, squares1.start); -testMotion('%_braces', ['%'], curlys1.end, curlys1.start); -testMotion('%_seek_outside', ['%'], seekOutside.end, seekOutside.start); -testMotion('%_seek_inside', ['%'], seekInside.end, seekInside.start); - -// Motion with folding tests -testMotionWithFolding('j_with_folding', 'j', foldingRangeDown.end, foldingRangeDown.start); -testMotionWithFolding('k_with_folding', 'k', foldingRangeUp.end, foldingRangeUp.start); - -testVim('%_seek_skip', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,9); -}, {value:'01234"("()'}); -testVim('%_skip_string', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,4); - cm.setCursor(0,2); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,0); -}, {value:'(")")'}); -testVim('%_skip_comment', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,6); - cm.setCursor(0,3); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,0); -}, {value:'(/*)*/)'}); -// Make sure that moving down after going to the end of a line always leaves you -// at the end of a line, but preserves the offset in other cases -testVim('Changing lines after Eol operation', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['$']); - helpers.doKeys(['j']); - // After moving to Eol and then down, we should be at Eol of line 2 - helpers.assertCursorAt(new Pos(1, lines[1].length - 1)); - helpers.doKeys(['j']); - // After moving down, we should be at Eol of line 3 - helpers.assertCursorAt(new Pos(2, lines[2].length - 1)); - helpers.doKeys(['h']); - helpers.doKeys(['j']); - // After moving back one space and then down, since line 4 is shorter than line 2, we should - // be at Eol of line 2 - 1 - helpers.assertCursorAt(new Pos(3, lines[3].length - 1)); - helpers.doKeys(['j']); - helpers.doKeys(['j']); - // After moving down again, since line 3 has enough characters, we should be back to the - // same place we were at on line 1 - helpers.assertCursorAt(new Pos(5, lines[2].length - 2)); -}); -//making sure gj and gk recover from clipping -testVim('gj_gk_clipping', function(cm,vim,helpers){ - cm.setCursor(0, 1); - helpers.doKeys('g','j','g','j'); - helpers.assertCursorAt(2, 1); - helpers.doKeys('g','k','g','k'); - helpers.assertCursorAt(0, 1); -},{value: 'line 1\n\nline 2'}); -//testing a mix of j/k and gj/gk -testVim('j_k_and_gj_gk', function(cm,vim,helpers){ - cm.setSize(120); - cm.setCursor(0, 0); - //go to the last character on the first line - helpers.doKeys('$'); - //move up/down on the column within the wrapped line - //side-effect: cursor is not locked to eol anymore - helpers.doKeys('g','k'); - var cur=cm.getCursor(); - eq(cur.line,0); - is((cur.ch<176),'gk didn\'t move cursor back (1)'); - helpers.doKeys('g','j'); - helpers.assertCursorAt(0, 176); - //should move to character 177 on line 2 (j/k preserve character index within line) - helpers.doKeys('j'); - //due to different line wrapping, the cursor can be on a different screen-x now - //gj and gk preserve screen-x on movement, much like moveV - helpers.doKeys('3','g','k'); - cur=cm.getCursor(); - eq(cur.line,1); - is((cur.ch<176),'gk didn\'t move cursor back (2)'); - helpers.doKeys('g','j','2','g','j'); - //should return to the same character-index - helpers.doKeys('k'); - helpers.assertCursorAt(0, 176); -},{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); -testVim('gj_gk', function(cm, vim, helpers) { - cm.setSize(120); - // Test top of document edge case. - cm.setCursor(0, 4); - helpers.doKeys('g', 'j'); - helpers.doKeys('10', 'g', 'k'); - helpers.assertCursorAt(0, 4); - - // Test moving down preserves column position. - helpers.doKeys('g', 'j'); - var pos1 = cm.getCursor(); - var expectedPos2 = new Pos(0, (pos1.ch - 4) * 2 + 4); - helpers.doKeys('g', 'j'); - helpers.assertCursorAt(expectedPos2); - - // Move to the last character - cm.setCursor(0, 0); - // Move left to reset HSPos - helpers.doKeys('h'); - // Test bottom of document edge case. - helpers.doKeys('100', 'g', 'j'); - var endingPos = cm.getCursor(); - is(endingPos != 0, 'gj should not be on wrapped line 0'); - var topLeftCharCoords = cm.charCoords(makeCursor(0, 0)); - var endingCharCoords = cm.charCoords(endingPos); - is(topLeftCharCoords.left == endingCharCoords.left, 'gj should end up on column 0'); -},{ lineNumbers: false, lineWrapping:true, value: 'Thislineisintentionallylongtotestmovementofgjandgkoverwrappedlines.' }); -testVim('}', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(1, 0); - cm.setCursor(0, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(4, 0); - cm.setCursor(0, 0); - helpers.doKeys('6', '}'); - helpers.assertCursorAt(5, 0); -}, { value: 'a\n\nb\nc\n\nd' }); -testVim('{', function(cm, vim, helpers) { - cm.setCursor(5, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(4, 0); - cm.setCursor(5, 0); - helpers.doKeys('2', '{'); - helpers.assertCursorAt(1, 0); - cm.setCursor(5, 0); - helpers.doKeys('6', '{'); - helpers.assertCursorAt(0, 0); -}, { value: 'a\n\nb\nc\n\nd' }); -testVim('(', function(cm, vim, helpers) { - cm.setCursor(6, 23); - helpers.doKeys('('); - helpers.assertCursorAt(6, 14); - helpers.doKeys('2', '('); - helpers.assertCursorAt(5, 0); - helpers.doKeys('('); - helpers.assertCursorAt(4, 0); - helpers.doKeys('('); - helpers.assertCursorAt(3, 0); - helpers.doKeys('('); - helpers.assertCursorAt(2, 0); - helpers.doKeys('('); - helpers.assertCursorAt(0, 0); - helpers.doKeys('('); - helpers.assertCursorAt(0, 0); -}, { value: 'sentence1.\n\n\nsentence2\n\nsentence3. sentence4\n sentence5? sentence6!' }); -testVim(')', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('2', ')'); - helpers.assertCursorAt(3, 0); - helpers.doKeys(')'); - helpers.assertCursorAt(4, 0); - helpers.doKeys(')'); - helpers.assertCursorAt(5, 0); - helpers.doKeys(')'); - helpers.assertCursorAt(5, 11); - helpers.doKeys(')'); - helpers.assertCursorAt(6, 14); - helpers.doKeys(')'); - helpers.assertCursorAt(6, 23); - helpers.doKeys(')'); - helpers.assertCursorAt(6, 23); -}, { value: 'sentence1.\n\n\nsentence2\n\nsentence3. sentence4\n sentence5? sentence6!' }); -testVim('paragraph_motions', function(cm, vim, helpers) { - cm.setCursor(10, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(4, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(7, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(16, 0); - - cm.setCursor(9, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(14, 0); - - cm.setCursor(6, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(7, 0); - - // ip inside empty space - cm.setCursor(10, 0); - helpers.doKeys('v', 'i', 'p'); - eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(12, 0), cm.getCursor('head')); - helpers.doKeys('i', 'p'); - eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(13, 1), cm.getCursor('head')); - helpers.doKeys('2', 'i', 'p'); - eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(16, 1), cm.getCursor('head')); - - // should switch to visualLine mode - cm.setCursor(14, 0); - helpers.doKeys('', 'v', 'i', 'p'); - helpers.assertCursorAt(14, 0); - - cm.setCursor(14, 0); - helpers.doKeys('', 'V', 'i', 'p'); - eqCursorPos(Pos(16, 1), cm.getCursor('head')); - - // ap inside empty space - cm.setCursor(10, 0); - helpers.doKeys('', 'v', 'a', 'p'); - eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(13, 1), cm.getCursor('head')); - helpers.doKeys('a', 'p'); - eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(16, 1), cm.getCursor('head')); - - cm.setCursor(13, 0); - helpers.doKeys('v', 'a', 'p'); - eqCursorPos(Pos(13, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(14, 0), cm.getCursor('head')); - - cm.setCursor(16, 0); - helpers.doKeys('v', 'a', 'p'); - eqCursorPos(Pos(14, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(16, 1), cm.getCursor('head')); - - cm.setCursor(0, 0); - helpers.doKeys('v', 'a', 'p'); - eqCursorPos(Pos(0, 0), cm.getCursor('anchor')); - eqCursorPos(Pos(4, 0), cm.getCursor('head')); - - cm.setCursor(0, 0); - helpers.doKeys('d', 'i', 'p'); - var register = helpers.getRegisterController().getRegister(); - eq('a\na\n', register.toString()); - is(register.linewise); - helpers.doKeys('3', 'j', 'p'); - helpers.doKeys('y', 'i', 'p'); - is(register.linewise); - eq('b\na\na\nc\n', register.toString()); -}, { value: 'a\na\n\n\n\nb\nc\n\n\n\n\n\n\nd\n\ne\nf' }); - -// Operator tests -testVim('dl', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('d', 'l'); - eq('word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dl_eol', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('d', 'l'); - eq(' word1', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 5); -}, { value: ' word1 ' }); -testVim('dl_repeat', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('2', 'd', 'l'); - eq('ord1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' w', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dh', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'h'); - eq(' wrd1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('o', register.toString()); - is(!register.linewise); - eqCursorPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dj', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'j'); - eq(' word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1\nword2\n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2\n word3' }); -testVim('dj_end_of_document', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'j'); - eq('', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1 \n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1 ' }); -testVim('dk', function(cm, vim, helpers) { - var curStart = makeCursor(1, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'k'); - eq(' word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1\nword2\n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2\n word3' }); -testVim('dk_start_of_document', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'k'); - eq('', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1 \n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1 ' }); -testVim('dw_space', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('d', 'w'); - eq('word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dw_word', function(cm, vim, helpers) { - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('d', 'w'); - eq(' word2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1 ', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1 word2' }); -testVim('dw_unicode_word', function(cm, vim, helpers) { - helpers.doKeys('d', 'w'); - eq(cm.getValue().length, 10); - helpers.doKeys('d', 'w'); - eq(cm.getValue().length, 6); - helpers.doKeys('d', 'w'); - eq(cm.getValue().length, 5); - helpers.doKeys('d', 'e'); - eq(cm.getValue().length, 2); -}, { value: ' \u0562\u0561\u0580\u0587\xbbe\xb5g ' }); -testVim('dw_only_word', function(cm, vim, helpers) { - // Test that if there is only 1 word left, dw deletes till the end of the - // line. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1 ', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1 ' }); -testVim('dw_eol', function(cm, vim, helpers) { - // Assert that dw does not delete the newline if last word to delete is at end - // of line. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' \nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1\nword2' }); -testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) { - // Assert that dw does not delete the newline if last word to delete is at end - // of line and it is followed by multiple newlines. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' \n\nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1\n\nword2' }); -testVim('dw_empty_line_followed_by_whitespace', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq(' \nword', cm.getValue()); -}, { value: '\n \nword' }); -testVim('dw_empty_line_followed_by_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('word', cm.getValue()); -}, { value: '\nword' }); -testVim('dw_empty_line_followed_by_empty_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n', cm.getValue()); -}, { value: '\n\n' }); -testVim('dw_whitespace_followed_by_whitespace', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n \n', cm.getValue()); -}, { value: ' \n \n' }); -testVim('dw_whitespace_followed_by_empty_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n\n', cm.getValue()); -}, { value: ' \n\n' }); -testVim('dw_word_whitespace_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n \nword2', cm.getValue()); -}, { value: 'word1\n \nword2'}) -testVim('dw_end_of_document', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('d', 'w'); - eq('\nab', cm.getValue()); -}, { value: '\nabc' }); -testVim('dw_repeat', function(cm, vim, helpers) { - // Assert that dw does delete newline if it should go to the next line, and - // that repeat works properly. - cm.setCursor(0, 1); - helpers.doKeys('d', '2', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 0); -}, { value: ' word1\nword2' }); -testVim('de_word_start_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'e'); - eq('\n\n', cm.getValue()); -}, { value: 'word\n\n' }); -testVim('de_word_end_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('d', 'e'); - eq('wor', cm.getValue()); -}, { value: 'word\n\n\n' }); -testVim('de_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'e'); - eq('', cm.getValue()); -}, { value: ' \n\n\n' }); -testVim('de_end_of_document', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('d', 'e'); - eq('\nab', cm.getValue()); -}, { value: '\nabc' }); -testVim('db_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('\n\n', cm.getValue()); -}, { value: '\n\n\n' }); -testVim('db_word_start_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('\nword', cm.getValue()); -}, { value: '\n\nword' }); -testVim('db_word_end_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 3); - helpers.doKeys('d', 'b'); - eq('\n\nd', cm.getValue()); -}, { value: '\n\nword' }); -testVim('db_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('', cm.getValue()); -}, { value: '\n \n' }); -testVim('db_start_of_document', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'b'); - eq('abc\n', cm.getValue()); -}, { value: 'abc\n' }); -testVim('dge_empty_lines', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doKeys('d', 'g', 'e'); - // Note: In real VIM the result should be '', but it's not quite consistent, - // since 2 newlines are deleted. But in the similar case of word\n\n, only - // 1 newline is deleted. We'll diverge from VIM's behavior since it's much - // easier this way. - eq('\n', cm.getValue()); -}, { value: '\n\n' }); -testVim('dge_word_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doKeys('d', 'g', 'e'); - eq('wor\n', cm.getValue()); -}, { value: 'word\n\n'}); -testVim('dge_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'g', 'e'); - eq('', cm.getValue()); -}, { value: '\n \n' }); -testVim('dge_start_of_document', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'g', 'e'); - eq('bc\n', cm.getValue()); -}, { value: 'abc\n' }); -testVim('d_inclusive', function(cm, vim, helpers) { - // Assert that when inclusive is set, the character the cursor is on gets - // deleted too. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('d', 'e'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('d_reverse', function(cm, vim, helpers) { - // Test that deleting in reverse works. - cm.setCursor(1, 0); - helpers.doKeys('d', 'b'); - eq(' word2 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\n', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2 ' }); -testVim('dd', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(1, 0)); - var expectedLineCount = cm.lineCount() - 1; - helpers.doKeys('d', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[1].textStart); -}); -testVim('dd_prefix_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(2, 0)); - var expectedLineCount = cm.lineCount() - 2; - helpers.doKeys('2', 'd', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[2].textStart); -}); -testVim('dd_motion_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(2, 0)); - var expectedLineCount = cm.lineCount() - 2; - helpers.doKeys('d', '2', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[2].textStart); -}); -testVim('dd_multiply_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(6, 0)); - var expectedLineCount = cm.lineCount() - 6; - helpers.doKeys('2', 'd', '3', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[6].textStart); -}); -testVim('dd_lastline', function(cm, vim, helpers) { - cm.setCursor(cm.lineCount(), 0); - var expectedLineCount = cm.lineCount() - 1; - helpers.doKeys('d', 'd'); - eq(expectedLineCount, cm.lineCount()); - helpers.assertCursorAt(cm.lineCount() - 1, 0); -}); -testVim('dd_only_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - var expectedRegister = cm.getValue() + "\n"; - helpers.doKeys('d','d'); - eq(1, cm.lineCount()); - eq('', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedRegister, register.toString()); -}, { value: "thisistheonlyline" }); -// Yank commands should behave the exact same as d commands, expect that nothing -// gets deleted. -testVim('yw_repeat', function(cm, vim, helpers) { - // Assert that yw does yank newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('y', '2', 'w'); - eq(' word1\nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2' }); -testVim('yy_multiply_repeat', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(6, 0)); - var expectedLineCount = cm.lineCount(); - helpers.doKeys('2', 'y', '3', 'y'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}); -testVim('2dd_blank_P', function(cm, vim, helpers) { - helpers.doKeys('2', 'd', 'd', 'P'); - eq('\na\n\n', cm.getValue()); -}, { value: '\na\n\n' }); -// Change commands behave like d commands except that it also enters insert -// mode. In addition, when the change is linewise, an additional newline is -// inserted so that insert mode starts on that line. -testVim('cw', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('c', '2', 'w'); - eq(' word3', cm.getValue()); - helpers.assertCursorAt(0, 0); -}, { value: 'word1 word2 word3'}); -testVim('cw_repeat', function(cm, vim, helpers) { - // Assert that cw does delete newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('c', '2', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: ' word1\nword2' }); -testVim('cc_multiply_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange(new Pos(0, 0), - new Pos(6, 0)); - var expectedLineCount = cm.lineCount() - 5; - helpers.doKeys('2', 'c', '3', 'c'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('ct', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('c', 't', 'w'); - eq(' word1 word3', cm.getValue()); - helpers.doKeys('', 'c', '|'); - eq(' word3', cm.getValue()); - helpers.assertCursorAt(0, 0); - helpers.doKeys('', '2', 'u', 'w', 'h'); - helpers.doKeys('c', '2', 'g', 'e'); - eq(' wordword3', cm.getValue()); -}, { value: ' word1 word2 word3'}); -testVim('cc_should_not_append_to_document', function(cm, vim, helpers) { - var expectedLineCount = cm.lineCount(); - cm.setCursor(cm.lastLine(), 0); - helpers.doKeys('c', 'c'); - eq(expectedLineCount, cm.lineCount()); -}); -function fillArray(val, times) { - var arr = []; - for (var i = 0; i < times; i++) { - arr.push(val); - } - return arr; -} -testVim('c_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'c'); - helpers.doKeys('hello'); - eq('1hello\n5hello\nahellofg', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(2, 3); - helpers.doKeys('', '2', 'k', 'h', 'C'); - helpers.doKeys('world'); - eq('1hworld\n5hworld\nahworld', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('c_visual_block_replay', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'c'); - helpers.doKeys('fo'); - eq('1fo4\n5fo8\nafodefg', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(0, 0); - helpers.doKeys('.'); - eq('foo4\nfoo8\nfoodefg', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('I_visual_block_replay', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys('', '2', 'j', 'l', 'I'); - helpers.doKeys('+-') - eq('12+-34\n56+-78\nab+-cdefg\nxyz', cm.getValue()); - helpers.doKeys(''); - // ensure that repeat location doesn't depend on last selection - cm.setCursor(3, 2); - helpers.doKeys('g', 'v') - eq("+-34\n+-78\n+-cd", cm.getSelection()) - cm.setCursor(0, 3); - helpers.doKeys('', '1', 'j', '2', 'l'); - eq("-34\n-78", cm.getSelection()); - cm.setCursor(0, 0); - eq("", cm.getSelection()); - helpers.doKeys('g', 'v'); - eq("-34\n-78", cm.getSelection()); - cm.setCursor(1, 1); - helpers.doKeys('.'); - eq('12+-34\n5+-6+-78\na+-b+-cdefg\nx+-yz', cm.getValue()); -}, {value: '1234\n5678\nabcdefg\nxyz'}); - -testVim('d_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'd'); - eq('1\n5\nafg', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('D_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'D'); - eq('1\n5\na', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); - -testVim('s_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 's'); - helpers.doKeys('hello{'); - eq('1hello{\n5hello{\nahello{fg\n', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(2, 3); - helpers.doKeys('', '1', 'k', 'h', 'S'); - helpers.doKeys('world'); - eq('1hello{\n world\n', cm.getValue()); -}, {value: '1234\n5678\nabcdefg\n'}); - -// Test mode change event. It should only fire once per mode transition. -testVim('on_mode_change', function(cm, vim, helpers) { - var modeHist = []; - function callback(arg) { - var subMode = arg.subMode ? ':' + arg.subMode : ''; - modeHist.push(arg.mode + subMode); - } - helpers.doKeys('', ''); - cm.on('vim-mode-change', callback); - function test(key, mode) { - modeHist.length = 0; - helpers.doKeys(key); - eq(modeHist.join(';'), mode); - } - test('v', 'visual'); - test('c', 'insert'); - test('', 'normal'); - test('', 'visual:blockwise'); - test('I', 'insert'); - test('', 'normal'); - test('R', 'replace'); - test('x', ''); - test('', 'normal'); - test('v', 'visual'); - test('V', 'visual:linewise'); - test('', 'visual:blockwise'); - test('v', 'visual'); - test('', 'normal'); - test('a', 'insert'); - test('', 'normal'); - test('v', 'visual'); - test(':', ''); // Event for Command-line mode not implemented. - test('y', 'normal'); -}); - -// Swapcase commands edit in place and do not modify registers. -testVim('g~w_repeat', function(cm, vim, helpers) { - // Assert that dw does delete newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('g', '~', '2', 'w'); - eq(' WORD1\nWORD2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2' }); -testVim('g~g~', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - var expectedLineCount = cm.lineCount(); - var expectedValue = cm.getValue().toUpperCase(); - helpers.doKeys('2', 'g', '~', '3', 'g', '~'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2\nword3\nword4\nword5\nword6' }); -testVim('gu_and_gU', function(cm, vim, helpers) { - var curStart = makeCursor(0, 7); - var value = cm.getValue(); - cm.setCursor(curStart); - helpers.doKeys('2', 'g', 'U', 'w'); - eq(cm.getValue(), 'wa wb xX WC wd'); - eqCursorPos(curStart, cm.getCursor()); - helpers.doKeys('2', 'g', 'u', 'w'); - eq(cm.getValue(), value); - - helpers.doKeys('2', 'g', 'U', 'B'); - eq(cm.getValue(), 'wa WB Xx wc wd'); - eqCursorPos(makeCursor(0, 3), cm.getCursor()); - - cm.setCursor(makeCursor(0, 4)); - helpers.doKeys('g', 'u', 'i', 'w'); - eq(cm.getValue(), 'wa wb Xx wc wd'); - eqCursorPos(makeCursor(0, 3), cm.getCursor()); - - // TODO: support gUgU guu - // eqCursorPos(makeCursor(0, 0), cm.getCursor()); - - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); -}, { value: 'wa wb xx wc wd' }); -testVim('visual_block_~', function(cm, vim, helpers) { - cm.setCursor(1, 1); - helpers.doKeys('', 'l', 'l', 'j', '~'); - helpers.assertCursorAt(1, 1); - eq('hello\nwoRLd\naBCDe', cm.getValue()); - cm.setCursor(2, 0); - helpers.doKeys('v', 'l', 'l', '~'); - helpers.assertCursorAt(2, 0); - eq('hello\nwoRLd\nAbcDe', cm.getValue()); -},{value: 'hello\nwOrld\nabcde' }); -testVim('._swapCase_visualBlock', function(cm, vim, helpers) { - helpers.doKeys('', 'j', 'j', 'l', '~'); - cm.setCursor(0, 3); - helpers.doKeys('.'); - eq('HelLO\nWorLd\nAbcdE', cm.getValue()); -},{value: 'hEllo\nwOrlD\naBcDe' }); -testVim('._delete_visualBlock', function(cm, vim, helpers) { - helpers.doKeys('', 'j', 'x'); - eq('ive\ne\nsome\nsugar', cm.getValue()); - helpers.doKeys('.'); - eq('ve\n\nsome\nsugar', cm.getValue()); - helpers.doKeys('j', 'j', '.'); - eq('ve\n\nome\nugar', cm.getValue()); - helpers.doKeys('u', '', '.'); - eq('ve\n\nme\ngar', cm.getValue()); -},{value: 'give\nme\nsome\nsugar' }); -testVim('>{motion}', function(cm, vim, helpers) { - cm.setCursor(1, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\n word2\nword3 '; - helpers.doKeys('>', 'k'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); -testVim('>>', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\n word2\nword3 '; - helpers.doKeys('2', '>', '>'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); -testVim('<{motion}', function(cm, vim, helpers) { - cm.setCursor(1, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('<', 'k'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); -testVim('<<', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('2', '<', '<'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); -testVim('=', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('', 'j', 'j'); - var expectedValue = 'word1\nword2\nword3'; - helpers.doKeys('='); - eq(expectedValue, cm.getValue()); -}, { value: ' word1\n word2\n word3', indentUnit: 2 }); - -// Edit tests - configureCm is an optional argument that gives caller -// access to the cm object. -function testEdit(name, before, pos, edit, after, configureCm) { - return testVim(name, function(cm, vim, helpers) { - if (configureCm) { - configureCm(cm); - } - var ch = before.search(pos) - var line = before.substring(0, ch).split('\n').length - 1; - if (line) { - ch = before.substring(0, ch).split('\n').pop().length; - } - cm.setCursor(line, ch); - helpers.doKeys.apply(this, edit.split('')); - eq(after, cm.getValue()); - }, {value: before}); -} - -// These Delete tests effectively cover word-wise Change, Visual & Yank. -// Tabs are used as differentiated whitespace to catch edge cases. -// Normal word: -testEdit('diw_mid_spc', 'foo \tbAr\t baz', /A/, 'diw', 'foo \t\t baz'); -testEdit('daw_mid_spc', 'foo \tbAr\t baz', /A/, 'daw', 'foo \tbaz'); -testEdit('diw_mid_punct', 'foo \tbAr.\t baz', /A/, 'diw', 'foo \t.\t baz'); -testEdit('daw_mid_punct', 'foo \tbAr.\t baz', /A/, 'daw', 'foo.\t baz'); -testEdit('diw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diw', 'foo \t,.\t baz'); -testEdit('daw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daw', 'foo \t,.\t baz'); -testEdit('diw_start_spc', 'bAr \tbaz', /A/, 'diw', ' \tbaz'); -testEdit('daw_start_spc', 'bAr \tbaz', /A/, 'daw', 'baz'); -testEdit('diw_start_punct', 'bAr. \tbaz', /A/, 'diw', '. \tbaz'); -testEdit('daw_start_punct', 'bAr. \tbaz', /A/, 'daw', '. \tbaz'); -testEdit('diw_end_spc', 'foo \tbAr', /A/, 'diw', 'foo \t'); -testEdit('daw_end_spc', 'foo \tbAr', /A/, 'daw', 'foo'); -testEdit('diw_end_punct', 'foo \tbAr.', /A/, 'diw', 'foo \t.'); -testEdit('daw_end_punct', 'foo \tbAr.', /A/, 'daw', 'foo.'); -// Big word: -testEdit('diW_mid_spc', 'foo \tbAr\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_spc', 'foo \tbAr\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_mid_punct', 'foo \tbAr.\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_punct', 'foo \tbAr.\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_start_spc', 'bAr\t baz', /A/, 'diW', '\t baz'); -testEdit('daW_start_spc', 'bAr\t baz', /A/, 'daW', 'baz'); -testEdit('diW_start_punct', 'bAr.\t baz', /A/, 'diW', '\t baz'); -testEdit('daW_start_punct', 'bAr.\t baz', /A/, 'daW', 'baz'); -testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t'); -testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo'); -testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t'); -testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo'); -// Deleting text objects -// Open and close on same line -testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz'); -testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz'); -testEdit('dib_open_spc', 'foo (bAr) baz', /\(/, 'dib', 'foo () baz'); -testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo baz'); -testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo baz'); - -testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz'); -testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz'); -testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo baz'); -testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo baz'); - -testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz'); -testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz'); -testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz'); -testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz'); - -testEdit('di`', 'foo `bAr` baz', /`/, 'di`', 'foo `` baz'); -testEdit('di>', 'foo baz', /', 'foo <> baz'); -testEdit('da<', 'foo baz', /b', /r/, 'di<', 'a\t<>b'); -testEdit('di>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'di>', 'a\t<>b'); -testEdit('da<_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da<', 'a\tb'); -testEdit('da>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da>', 'a\tb'); - -// deleting tag objects -testEdit('dat_noop', 'hello', /n/, 'dat', 'hello'); -testEdit('dat_open_tag', 'hello', /n/, 'dat', '', function(cm) { - cm.setOption('mode', 'xml'); -}); -testEdit('dat_inside_tag', 'hello', /l/, 'dat', '', function(cm) { - cm.setOption('mode', 'xml'); -}); -testEdit('dat_close_tag', 'hello', /\//, 'dat', '', function(cm) { - cm.setOption('mode', 'xml'); -}); - -testEdit('dit_open_tag', 'hello', /n/, 'dit', '', function(cm) { - cm.setOption('mode', 'xml'); -}); -testEdit('dit_inside_tag', 'hello', /l/, 'dit', '', function(cm) { - cm.setOption('mode', 'xml'); -}); -testEdit('dit_close_tag', 'hello', /\//, 'dit', '', function(cm) { - cm.setOption('mode', 'xml'); -}); - -function testSelection(name, before, pos, keys, sel) { - return testVim(name, function(cm, vim, helpers) { - var ch = before.search(pos) - var line = before.substring(0, ch).split('\n').length - 1; - if (line) { - ch = before.substring(0, ch).split('\n').pop().length; - } - cm.setCursor(line, ch); - helpers.doKeys.apply(this, keys.split('')); - eq(sel, cm.getSelection()); - }, {value: before}); -} -testSelection('viw_middle_spc', 'foo \tbAr\t baz', /A/, 'viw', 'bAr'); -testSelection('vaw_middle_spc', 'foo \tbAr\t baz', /A/, 'vaw', 'bAr\t '); -testSelection('viw_middle_punct', 'foo \tbAr,\t baz', /A/, 'viw', 'bAr'); -testSelection('vaW_middle_punct', 'foo \tbAr,\t baz', /A/, 'vaW', 'bAr,\t '); -testSelection('viw_start_spc', 'foo \tbAr\t baz', /b/, 'viw', 'bAr'); -testSelection('viw_end_spc', 'foo \tbAr\t baz', /r/, 'viw', 'bAr'); -testSelection('viw_eol', 'foo \tbAr', /r/, 'viw', 'bAr'); -testSelection('vi{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'vi{', '\n\tbar\n\t'); -testSelection('va{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'va{', '{\n\tbar\n\t}'); - -testVim('mouse_select', function(cm, vim, helpers) { - cm.setSelection(Pos(0, 2), Pos(0, 4), {origin: '*mouse'}); - is(cm.state.vim.visualMode); - is(!cm.state.vim.visualLine); - is(!cm.state.vim.visualBlock); - helpers.doKeys(''); - is(!cm.somethingSelected()); - helpers.doKeys('g', 'v'); - eq('cd', cm.getSelection()); -}, {value: 'abcdef'}); - -// Operator-motion tests -testVim('D', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('D'); - eq(' wo\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('rd1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 2); -}, { value: ' word1\nword2\n word3' }); -testVim('C', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('C'); - eq(' wo\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('rd1', register.toString()); - is(!register.linewise); - eqCursorPos(curStart, cm.getCursor()); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: ' word1\nword2\n word3' }); -testVim('Y', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('Y'); - eq(' word1\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1\n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\n word3' }); -testVim('Yy_blockwise', function(cm, vim, helpers) { - helpers.doKeys('', 'j', '2', 'l', 'Y'); - helpers.doKeys('G', 'p', 'g', 'g'); - helpers.doKeys('', 'j', '2', 'l', 'y'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('$', 'p'); - eq('123456123\n123456123\n123456\n123456', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('123\n123', register.toString()); - is(register.blockwise); - helpers.assertCursorAt(0, 6); - helpers.doKeys('$', 'j', 'p'); - helpers.doKeys('$', 'j', 'P'); - eq("123456123\n123456123123\n123456 121233\n123456 123", cm.getValue()); -}, { value: '123456\n123456\n' }); -testVim('~', function(cm, vim, helpers) { - helpers.doKeys('3', '~'); - eq('ABCdefg', cm.getValue()); - helpers.assertCursorAt(0, 3); -}, { value: 'abcdefg' }); - -// Action tests -testVim('ctrl-a', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('-9', cm.getValue()); - helpers.assertCursorAt(0, 1); - helpers.doKeys('2',''); - eq('-7', cm.getValue()); -}, {value: '-10'}); -testVim('ctrl-x', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('-1', cm.getValue()); - helpers.assertCursorAt(0, 1); - helpers.doKeys('2',''); - eq('-3', cm.getValue()); -}, {value: '0'}); -testVim('/ search forward', function(cm, vim, helpers) { - forEach(['', ''], function(key) { - cm.setCursor(0, 0); - helpers.doKeys(key); - helpers.assertCursorAt(0, 5); - helpers.doKeys('l'); - helpers.doKeys(key); - helpers.assertCursorAt(0, 10); - cm.setCursor(0, 11); - helpers.doKeys(key); - helpers.assertCursorAt(0, 11); - }); -}, {value: '__jmp1 jmp2 jmp'}); -testVim('insert_ctrl_w', function(cm, vim, helpers) { - var curStart = makeCursor(0, 10); - cm.setCursor(curStart); - helpers.doKeys('a'); - helpers.doKeys(''); - eq('word1/', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word2', register.toString()); - is(!register.linewise); - var curEnd = makeCursor(0, 6); - eqCursorPos(curEnd, cm.getCursor()); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'word1/word2' }); -testVim('normal_ctrl_w', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys(''); - eq('word', cm.getValue()); - var curEnd = makeCursor(0, 3); - helpers.assertCursorAt(0,3); - eqCursorPos(curEnd, cm.getCursor()); - eq('vim', cm.getOption('keyMap')); -}, {value: 'word'}); -testVim('a', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('a'); - helpers.assertCursorAt(0, 2); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('a_eol', function(cm, vim, helpers) { - cm.setCursor(0, lines[0].length - 1); - helpers.doKeys('a'); - helpers.assertCursorAt(0, lines[0].length); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('A_endOfSelectedArea', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'j', 'l'); - helpers.doKeys('A'); - helpers.assertCursorAt(1, 2); - eq('vim-insert', cm.getOption('keyMap')); -}, {value: 'foo\nbar'}); -testVim('i', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('i'); - helpers.assertCursorAt(0, 1); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('i_repeat', function(cm, vim, helpers) { - helpers.doKeys('3', 'i'); - helpers.doKeys('test') - helpers.doKeys(''); - eq('testtesttest', cm.getValue()); - helpers.assertCursorAt(0, 11); -}, { value: '' }); -testVim('i_repeat_delete', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('2', 'i'); - helpers.doKeys('z') - helpers.doInsertModeKeys('Backspace', 'Backspace'); - helpers.doKeys(''); - eq('abe', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'abcde' }); -testVim('insert', function(cm, vim, helpers) { - helpers.doKeys('i'); - eq('vim-insert', cm.getOption('keyMap')); - eq(false, cm.state.overwrite); - helpers.doKeys(''); - eq('vim-replace', cm.getOption('keyMap')); - eq(true, cm.state.overwrite); - helpers.doKeys(''); - eq('vim-insert', cm.getOption('keyMap')); - eq(false, cm.state.overwrite); -}); -testVim('i_backspace', function(cm, vim, helpers) { - cm.setCursor(0, 10); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Backspace'); - helpers.assertCursorAt(0, 9); - eq('012345678', cm.getValue()); -}, { value: '0123456789'}); -testVim('i_overwrite_backspace', function(cm, vim, helpers) { - cm.setCursor(0, 10); - helpers.doKeys('i'); - helpers.doKeys(''); - helpers.doInsertModeKeys('Backspace'); - helpers.assertCursorAt(Pos(0, 9, "after")); - eq('0123456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('i_forward_delete', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Delete'); - helpers.assertCursorAt(0, 3); - eq('A124\nBCD', cm.getValue()); - helpers.doInsertModeKeys('Delete'); - helpers.assertCursorAt(0, 3); - eq('A12\nBCD', cm.getValue()); - helpers.doInsertModeKeys('Delete'); - helpers.assertCursorAt(0, 3); - eq('A12BCD', cm.getValue()); -}, { value: 'A1234\nBCD'}); -testVim('forward_delete', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys(''); - helpers.assertCursorAt(0, 3); - eq('A124\nBCD', cm.getValue()); - helpers.doKeys(''); - helpers.assertCursorAt(0, 2); - eq('A12\nBCD', cm.getValue()); - helpers.doKeys(''); - helpers.assertCursorAt(0, 1); - eq('A1\nBCD', cm.getValue()); -}, { value: 'A1234\nBCD'}); -testVim('A', function(cm, vim, helpers) { - helpers.doKeys('A'); - helpers.assertCursorAt(0, lines[0].length); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('A_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'A'); - helpers.doKeys('hello'); - eq('testhello\nmehello\npleahellose', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(0, 0); - helpers.doKeys('.'); - // TODO this doesn't work yet - // eq('teshellothello\nme hello hello\nplehelloahellose', cm.getValue()); -}, {value: 'test\nme\nplease'}); -testVim('I', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('I'); - helpers.assertCursorAt(0, lines[0].textStart); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('I_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('3', 'I'); - helpers.doKeys('test') - helpers.doKeys(''); - eq('testtesttestblah', cm.getValue()); - helpers.assertCursorAt(0, 11); -}, { value: 'blah' }); -testVim('I_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'l', 'l', 'I'); - helpers.doKeys('hello'); - eq('hellotest\nhellome\nhelloplease', cm.getValue()); -}, {value: 'test\nme\nplease'}); -testVim('o', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('o'); - eq('word1\n\nword2', cm.getValue()); - helpers.assertCursorAt(1, 0); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'word1\nword2' }); -testVim('o_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('3', 'o'); - helpers.doKeys('test') - helpers.doKeys(''); - eq('\ntest\ntest\ntest', cm.getValue()); - helpers.assertCursorAt(3, 3); -}, { value: '' }); -testVim('O', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('O'); - eq('\nword1\nword2', cm.getValue()); - helpers.assertCursorAt(0, 0); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'word1\nword2' }); -testVim('J', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('J'); - var expectedValue = 'word1 word2\nword3\n word4'; - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, expectedValue.indexOf('word2') - 1); -}, { value: 'word1 \n word2\nword3\n word4' }); -testVim('J_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('3', 'J'); - var expectedValue = 'word1 word2 word3\n word4'; - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, expectedValue.indexOf('word3') - 1); -}, { value: 'word1 \n word2\nword3\n word4' }); -testVim('gJ', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('g', 'J'); - eq('word1word2 \n word3', cm.getValue()); - helpers.assertCursorAt(0, 5); - helpers.doKeys('g', 'J'); - eq('word1word2 word3', cm.getValue()); - helpers.assertCursorAt(0, 11); -}, { value: 'word1\nword2 \n word3' }); -testVim('gi', function(cm, vim, helpers) { - cm.setCursor(1, 5); - helpers.doKeys('g', 'I'); - helpers.doKeys('a', 'a', '', 'k'); - eq('12\naa xxxx', cm.getValue()); - helpers.assertCursorAt(0, 1); - helpers.doKeys('g', 'i'); - helpers.assertCursorAt(1, 2); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: '12\n xxxx' }); -testVim('p', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); - helpers.doKeys('p'); - eq('__abc\ndef_', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_register', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().getRegister('a').setText('abc\ndef', false); - helpers.doKeys('"', 'a', 'p'); - eq('__abc\ndef_', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_wrong_register', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().getRegister('a').setText('abc\ndef', false); - helpers.doKeys('p'); - eq('___', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: '___' }); -testVim('p_line', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); - helpers.doKeys('2', 'p'); - eq('___\n a\nd\n a\nd', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_lastline', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd', true); - helpers.doKeys('2', 'p'); - eq('___\n a\nd\n a\nd', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim(']p_first_indent_is_smaller', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq(' ___\n abc\n def', cm.getValue()); -}, { value: ' ___' }); -testVim(']p_first_indent_is_larger', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq(' ___\n abc\ndef', cm.getValue()); -}, { value: ' ___' }); -testVim(']p_with_tab_indents', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', '\t\tabc\n\t\t\tdef\n', true); - helpers.doKeys(']', 'p'); - eq('\t___\n\tabc\n\t\tdef', cm.getValue()); -}, { value: '\t___', indentWithTabs: true}); -testVim(']p_with_spaces_translated_to_tabs', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq('\t___\n\tabc\n\t\tdef', cm.getValue()); -}, { value: '\t___', indentWithTabs: true, tabSize: 2 }); -testVim('[p', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys('[', 'p'); - eq(' abc\n def\n ___', cm.getValue()); -}, { value: ' ___' }); -testVim('P', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); - helpers.doKeys('P'); - eq('_abc\ndef__', cm.getValue()); - helpers.assertCursorAt(1, 3); -}, { value: '___' }); -testVim('P_line', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); - helpers.doKeys('2', 'P'); - eq(' a\nd\n a\nd\n___', cm.getValue()); - helpers.assertCursorAt(0, 2); -}, { value: '___' }); -testVim('r', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('3', 'r', 'u'); - eq('wuuuet\nanother', cm.getValue(),'3r failed'); - helpers.assertCursorAt(0, 3); - cm.setCursor(0, 4); - helpers.doKeys('v', 'j', 'h', 'r', ''); - eq('wuuu \n her', cm.getValue(),'Replacing selection by space-characters failed'); - cm.setValue("ox"); - helpers.doKeys('r', ''); - eq('ox', cm.getValue()); - helpers.doKeys('r', ''); - eq('ox', cm.getValue()); - helpers.doKeys('r', ''); - eq('\nx', cm.getValue()); -}, { value: 'wordet\nanother' }); -testVim('r_visual_block', function(cm, vim, helpers) { - cm.setCursor(2, 3); - helpers.doKeys('', 'k', 'k', 'h', 'h', 'r', 'l'); - eq('1lll\n5lll\nalllefg', cm.getValue()); - helpers.doKeys('', 'l', 'j', 'r', ''); - eq('1 l\n5 l\nalllefg', cm.getValue()); - cm.setCursor(2, 0); - helpers.doKeys('o'); - helpers.doKeys('\t\t') - helpers.doKeys(''); - helpers.doKeys('', 'h', 'h', 'r', 'r'); - eq('1 l\n5 l\nalllefg\nrrrrrrrr', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('R', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('R'); - helpers.assertCursorAt(0, 1); - eq('vim-replace', cm.getOption('keyMap')); - is(cm.state.overwrite, 'Setting overwrite state failed'); -}); -testVim('R_visual', function(cm, vim, helpers) { - helpers.doKeys('', 'j', 'R', '0', ''); - eq('0\nb33\nc44\nc55', cm.getValue()); - helpers.doKeys('2', 'j', '.'); - eq('0\nb33\n0', cm.getValue()); - helpers.doKeys('k', 'v', 'R', '1', ''); - eq('0\n1\n0', cm.getValue()); - helpers.doKeys('k', '.'); - eq('1\n1\n0', cm.getValue()); - helpers.doKeys('p'); - eq('1\n0\n1\n0', cm.getValue()); -}, {value: 'a11\na22\nb33\nc44\nc55'}); -testVim('mark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('`', 't'); - helpers.assertCursorAt(2, 2); - cm.setCursor(2, 0); - cm.replaceRange(' h', cm.getCursor()); - cm.setCursor(0, 0); - helpers.doKeys('\'', 't'); - helpers.assertCursorAt(2, 3); -}); -testVim('mark\'', function(cm, vim, helpers) { - // motions that do not update jumplist - cm.setCursor(2, 2); - helpers.doKeys('`', '\''); - helpers.assertCursorAt(0, 0); - helpers.doKeys('j', '3', 'l'); - helpers.doKeys('`', '`'); - helpers.assertCursorAt(2, 2); - helpers.doKeys('`', '`'); - helpers.assertCursorAt(1, 3); - // motions that update jumplist - cm.openDialog = helpers.fakeOpenDialog('='); - helpers.doKeys('/'); - helpers.assertCursorAt(6, 20); - helpers.doKeys('`', '`'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('\'', '\''); - helpers.assertCursorAt(6, 2); - helpers.doKeys('\'', '`'); - helpers.assertCursorAt(1, 1); - // edits - helpers.doKeys('g', 'I', '\n', '', 'l'); - helpers.doKeys('`', '`'); - helpers.assertCursorAt(7, 2); - helpers.doKeys('`', '`'); - helpers.assertCursorAt(2, 1); -}); -testVim('mark.', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('O', 'testing', ''); - cm.setCursor(3, 3); - helpers.doKeys('\'', '.'); - helpers.assertCursorAt(0, 0); - cm.setCursor(4, 4); - helpers.doKeys('`', '.'); - helpers.assertCursorAt(0, 6); -}); -testVim('jumpToMark_next', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 2); - cm.setCursor(0, 0); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_next_repeat', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(0, 0); - helpers.doKeys('2', ']', '`'); - helpers.assertCursorAt(3, 2); - cm.setCursor(0, 0); - helpers.doKeys('2', ']', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_next_sameline', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 2); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 4); -}); -testVim('jumpToMark_next_onlyprev', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(4, 0); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(4, 0); -}); -testVim('jumpToMark_next_nomark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 2); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_next_linewise_over', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 1); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_next_action', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('d', ']', '`'); - helpers.assertCursorAt(0, 0); - var actual = cm.getLine(0); - var expected = 'pop pop 0 1 2 3 4'; - eq(actual, expected, "Deleting while jumping to the next mark failed."); -}); -testVim('jumpToMark_next_line_action', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('d', ']', '\''); - helpers.assertCursorAt(0, 1); - var actual = cm.getLine(0); - var expected = ' (a) [b] {c} ' - eq(actual, expected, "Deleting while jumping to the next mark line failed."); -}); -testVim('jumpToMark_prev', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(4, 0); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 2); - cm.setCursor(4, 0); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_repeat', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(5, 0); - helpers.doKeys('2', '[', '`'); - helpers.assertCursorAt(3, 2); - cm.setCursor(5, 0); - helpers.doKeys('2', '[', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_prev_sameline', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 2); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_onlynext', function(cm, vim, helpers) { - cm.setCursor(4, 4); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 0); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_nomark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 2); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_linewise_over', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 6); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('delmark_single', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 't'); - helpers.doEx('delmarks t'); - cm.setCursor(0, 0); - helpers.doKeys('`', 't'); - helpers.assertCursorAt(0, 0); -}); -testVim('delmark_range', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks b-d'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_multi', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks bcd'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_multi_space', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks b c d'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_all', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks a b-de'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(0, 0); -}); -testVim('visual', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l'); - helpers.assertCursorAt(0, 4); - eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor')); - helpers.doKeys('d'); - eq('15', cm.getValue()); -}, { value: '12345' }); -testVim('visual_yank', function(cm, vim, helpers) { - helpers.doKeys('v', '3', 'l', 'y'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('p'); - eq('aa te test for yank', cm.getValue()); -}, { value: 'a test for yank' }) -testVim('visual_w', function(cm, vim, helpers) { - helpers.doKeys('v', 'w'); - eq(cm.getSelection(), 'motion t'); -}, { value: 'motion test'}); -testVim('visual_initial_selection', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v'); - cm.getSelection('n'); -}, { value: 'init'}); -testVim('visual_crossover_left', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys('v', 'l', 'h', 'h'); - cm.getSelection('ro'); -}, { value: 'cross'}); -testVim('visual_crossover_left', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys('v', 'h', 'l', 'l'); - cm.getSelection('os'); -}, { value: 'cross'}); -testVim('visual_crossover_up', function(cm, vim, helpers) { - cm.setCursor(3, 2); - helpers.doKeys('v', 'j', 'k', 'k'); - eqCursorPos(Pos(2, 2), cm.getCursor('head')); - eqCursorPos(Pos(3, 3), cm.getCursor('anchor')); - helpers.doKeys('k'); - eqCursorPos(Pos(1, 2), cm.getCursor('head')); - eqCursorPos(Pos(3, 3), cm.getCursor('anchor')); -}, { value: 'cross\ncross\ncross\ncross\ncross\n'}); -testVim('visual_crossover_down', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('v', 'k', 'j', 'j'); - eqCursorPos(Pos(2, 3), cm.getCursor('head')); - eqCursorPos(Pos(1, 2), cm.getCursor('anchor')); - helpers.doKeys('j'); - eqCursorPos(Pos(3, 3), cm.getCursor('head')); - eqCursorPos(Pos(1, 2), cm.getCursor('anchor')); -}, { value: 'cross\ncross\ncross\ncross\ncross\n'}); -testVim('visual_exit', function(cm, vim, helpers) { - helpers.doKeys('', 'l', 'j', 'j', ''); - eqCursorPos(cm.getCursor('anchor'), cm.getCursor('head')); - eq(vim.visualMode, false); -}, { value: 'hello\nworld\nfoo' }); -testVim('visual_line', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'l', 'j', 'j', 'd'); - eq(' 4\n 5', cm.getValue()); -}, { value: ' 1\n 2\n 3\n 4\n 5' }); -testVim('visual_block_move_to_eol', function(cm, vim, helpers) { - // moveToEol should move all block cursors to end of line - cm.setCursor(0, 0); - helpers.doKeys('', 'G', '$'); - var selections = cm.getSelections().join(); - eq('123,45,6', selections); - // Checks that with cursor at Infinity, finding words backwards still works. - helpers.doKeys('2', 'k', 'b'); - selections = cm.getSelections().join(); - eq('1', selections); -}, {value: '123\n45\n6'}); -testVim('visual_block_different_line_lengths', function(cm, vim, helpers) { - // test the block selection with lines of different length - // i.e. extending the selection - // till the end of the longest line. - helpers.doKeys('', 'l', 'j', 'j', '6', 'l', 'd'); - helpers.doKeys('d', 'd', 'd', 'd'); - eq('', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('visual_block_truncate_on_short_line', function(cm, vim, helpers) { - // check for left side selection in case - // of moving up to a shorter line. - cm.replaceRange('', cm.getCursor()); - cm.setCursor(3, 4); - helpers.doKeys('', 'l', 'k', 'k', 'd'); - eq('hello world\n{\ntis\nsa!', cm.getValue()); -}, {value: 'hello world\n{\nthis is\nsparta!'}); -testVim('visual_block_corners', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('', '2', 'l', 'k'); - // circle around the anchor - // and check the selections - var selections = cm.getSelections(); - eq('345891', selections.join('')); - helpers.doKeys('4', 'h'); - selections = cm.getSelections(); - eq('123678', selections.join('')); - helpers.doKeys('j', 'j'); - selections = cm.getSelections(); - eq('678abc', selections.join('')); - helpers.doKeys('4', 'l'); - selections = cm.getSelections(); - eq('891cde', selections.join('')); -}, {value: '12345\n67891\nabcde'}); -testVim('visual_block_mode_switch', function(cm, vim, helpers) { - // switch between visual modes - cm.setCursor(1, 1); - // blockwise to characterwise visual - helpers.doKeys('', 'j', 'l', 'v'); - var selections = cm.getSelections(); - eq('7891\nabc', selections.join('')); - // characterwise to blockwise - helpers.doKeys(''); - selections = cm.getSelections(); - eq('78bc', selections.join('')); - // blockwise to linewise visual - helpers.doKeys('V'); - selections = cm.getSelections(); - eq('67891\nabcde', selections.join('')); -}, {value: '12345\n67891\nabcde'}); -testVim('visual_block_crossing_short_line', function(cm, vim, helpers) { - // visual block with long and short lines - cm.setCursor(0, 3); - helpers.doKeys('', 'j', 'j', 'j'); - var selections = cm.getSelections().join(); - eq('4,,d,b', selections); - helpers.doKeys('3', 'k'); - selections = cm.getSelections().join(); - eq('4', selections); - helpers.doKeys('5', 'j', 'k'); - selections = cm.getSelections().join(""); - eq(10, selections.length); -}, {value: '123456\n78\nabcdefg\nfoobar\n}\n'}); -testVim('visual_block_curPos_on_exit', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '3' , 'l', ''); - eqCursorPos(makeCursor(0, 3), cm.getCursor()); - helpers.doKeys('h', '', '2' , 'j' ,'3' , 'l'); - eq(cm.getSelections().join(), "3456,,cdef"); - helpers.doKeys('4' , 'h'); - eq(cm.getSelections().join(), "23,8,bc"); - helpers.doKeys('2' , 'l'); - eq(cm.getSelections().join(), "34,,cd"); -}, {value: '123456\n78\nabcdefg\nfoobar'}); - -testVim('visual_marks', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l', 'j', 'j', 'v'); - // Test visual mode marks - cm.setCursor(2, 1); - helpers.doKeys('\'', '<'); - helpers.assertCursorAt(0, 1); - helpers.doKeys('\'', '>'); - helpers.assertCursorAt(2, 0); -}); -testVim('visual_join', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'l', 'j', 'j', 'J'); - eq(' 1 2 3\n 4\n 5', cm.getValue()); - is(!vim.visualMode); -}, { value: ' 1\n 2\n 3\n 4\n 5' }); -testVim('visual_join_2', function(cm, vim, helpers) { - helpers.doKeys('G', 'V', 'g', 'g', 'J'); - eq('1 2 3 4 5 6 ', cm.getValue()); - is(!vim.visualMode); -}, { value: '1\n2\n3\n4\n5\n6\n'}); -testVim('visual_blank', function(cm, vim, helpers) { - helpers.doKeys('v', 'k'); - eq(vim.visualMode, true); -}, { value: '\n' }); -testVim('reselect_visual', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l', 'l', 'y', 'g', 'v'); - helpers.assertCursorAt(0, 5); - eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor')); - helpers.doKeys('v'); - cm.setCursor(1, 0); - helpers.doKeys('v', 'l', 'l', 'p'); - eq('123456\n2345\nbar', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys('g', 'v'); - // here the fake cursor is at (1, 3) - helpers.assertCursorAt(1, 4); - eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor')); - helpers.doKeys('v'); - cm.setCursor(2, 0); - helpers.doKeys('v', 'l', 'l', 'g', 'v'); - helpers.assertCursorAt(1, 4); - eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor')); - helpers.doKeys('g', 'v'); - helpers.assertCursorAt(2, 3); - eqCursorPos(makeCursor(2, 0), cm.getCursor('anchor')); - eq('123456\n2345\nbar', cm.getValue()); -}, { value: '123456\nfoo\nbar' }); -testVim('reselect_visual_line', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'j', 'j', 'V', 'g', 'v', 'd'); - eq('foo\nand\nbar', cm.getValue()); - cm.setCursor(1, 0); - helpers.doKeys('V', 'y', 'j'); - helpers.doKeys('V', 'p' , 'g', 'v', 'd'); - eq('foo\nand', cm.getValue()); -}, { value: 'hello\nthis\nis\nfoo\nand\nbar' }); -testVim('reselect_visual_block', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('', 'k', 'h', ''); - cm.setCursor(2, 1); - helpers.doKeys('v', 'l', 'g', 'v'); - eqCursorPos(Pos(1, 2), vim.sel.anchor); - eqCursorPos(Pos(0, 1), vim.sel.head); - // Ensure selection is done with visual block mode rather than one - // continuous range. - eq(cm.getSelections().join(''), '23oo') - helpers.doKeys('g', 'v'); - eqCursorPos(Pos(2, 1), vim.sel.anchor); - eqCursorPos(Pos(2, 2), vim.sel.head); - helpers.doKeys(''); - // Ensure selection of deleted range - cm.setCursor(1, 1); - helpers.doKeys('v', '', 'j', 'd', 'g', 'v'); - eq(cm.getSelections().join(''), 'or'); -}, { value: '123456\nfoo\nbar' }); -testVim('s_normal', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('s'); - helpers.doKeys(''); - eq('ac', cm.getValue()); -}, { value: 'abc'}); -testVim('s_visual', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v', 's'); - helpers.doKeys(''); - helpers.assertCursorAt(0, 0); - eq('ac', cm.getValue()); -}, { value: 'abc'}); -testVim('o_visual', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys('v','l','l','l','o'); - helpers.assertCursorAt(0,0); - helpers.doKeys('v','v','j','j','j','o'); - helpers.assertCursorAt(0,0); - helpers.doKeys('O'); - helpers.doKeys('l','l') - helpers.assertCursorAt(3, 3); - helpers.doKeys('d'); - eq('p',cm.getValue()); -}, { value: 'abcd\nefgh\nijkl\nmnop'}); -testVim('o_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('','3','j','l','l', 'o'); - eqCursorPos(Pos(3, 3), vim.sel.anchor); - eqCursorPos(Pos(0, 1), vim.sel.head); - helpers.doKeys('O'); - eqCursorPos(Pos(3, 1), vim.sel.anchor); - eqCursorPos(Pos(0, 3), vim.sel.head); - helpers.doKeys('o'); - eqCursorPos(Pos(0, 3), vim.sel.anchor); - eqCursorPos(Pos(3, 1), vim.sel.head); -}, { value: 'abcd\nefgh\nijkl\nmnop'}); -testVim('changeCase_visual', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'l', 'l'); - helpers.doKeys('U'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('v', 'l', 'l'); - helpers.doKeys('u'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('l', 'l', 'l', '.'); - helpers.assertCursorAt(0, 3); - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'v', 'j', 'U', 'q'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('j', '@', 'a'); - helpers.assertCursorAt(1, 0); - cm.setCursor(3, 0); - helpers.doKeys('V', 'U', 'j', '.'); - eq('ABCDEF\nGHIJKL\nMnopq\nSHORT LINE\nLONG LINE OF TEXT', cm.getValue()); -}, { value: 'abcdef\nghijkl\nmnopq\nshort line\nlong line of text'}); -testVim('changeCase_visual_block', function(cm, vim, helpers) { - cm.setCursor(2, 1); - helpers.doKeys('', 'k', 'k', 'h', 'U'); - eq('ABcdef\nGHijkl\nMNopq\nfoo', cm.getValue()); - cm.setCursor(0, 2); - helpers.doKeys('.'); - eq('ABCDef\nGHIJkl\nMNOPq\nfoo', cm.getValue()); - // check when last line is shorter. - cm.setCursor(2, 2); - helpers.doKeys('.'); - eq('ABCDef\nGHIJkl\nMNOPq\nfoO', cm.getValue()); -}, { value: 'abcdef\nghijkl\nmnopq\nfoo'}); -testVim('visual_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'l', 'l', 'y'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('3', 'l', 'j', 'v', 'l', 'p'); - helpers.assertCursorAt(1, 5); - eq('this is a\nunithitest for visual paste', cm.getValue()); - cm.setCursor(0, 0); - // in case of pasting whole line - helpers.doKeys('y', 'y'); - cm.setCursor(1, 6); - helpers.doKeys('v', 'l', 'l', 'l', 'p'); - helpers.assertCursorAt(2, 0); - eq('this is a\nunithi\nthis is a\n for visual paste', cm.getValue()); -}, { value: 'this is a\nunit test for visual paste'}); - -// This checks the contents of the register used to paste the text -testVim('v_paste_from_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - cm.setCursor(1, 0); - helpers.doKeys('v', 'p'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+register/.test(text)); - }); -}, { value: 'register contents\nare not erased'}); -testVim('S_normal', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('j', 'S'); - helpers.doKeys(''); - helpers.assertCursorAt(1, 1); - eq('aa{\n \ncc', cm.getValue()); - helpers.doKeys('j', 'S'); - eq('aa{\n \n ', cm.getValue()); - helpers.assertCursorAt(2, 2); - helpers.doKeys(''); - helpers.doKeys('d', 'd', 'd', 'd'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('S'); - is(vim.insertMode); - eq('', cm.getValue()); -}, { value: 'aa{\nbb\ncc'}); -testVim('blockwise_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '3', 'j', 'l', 'y'); - cm.setCursor(0, 2); - // paste one char after the current cursor position - helpers.doKeys('p'); - eq('helhelo\nworwold\nfoofo\nbarba', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys('v', '4', 'l', 'y'); - cm.setCursor(0, 0); - helpers.doKeys('', '3', 'j', 'p'); - eq('helheelhelo\norwold\noofo\narba', cm.getValue()); -}, { value: 'hello\nworld\nfoo\nbar'}); -testVim('blockwise_paste_long/short_line', function(cm, vim, helpers) { - // extend short lines in case of different line lengths. - cm.setCursor(0, 0); - helpers.doKeys('', 'j', 'j', 'y'); - cm.setCursor(0, 3); - helpers.doKeys('p'); - eq('hellho\nfoo f\nbar b', cm.getValue()); -}, { value: 'hello\nfoo\nbar'}); -testVim('blockwise_paste_cut_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'x'); - cm.setCursor(0, 0); - helpers.doKeys('P'); - eq('cut\nand\npaste\nme', cm.getValue()); -}, { value: 'cut\nand\npaste\nme'}); -testVim('blockwise_paste_from_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', '"', 'a', 'y'); - cm.setCursor(0, 3); - helpers.doKeys('"', 'a', 'p'); - eq('foobfar\nhellho\nworlwd', cm.getValue()); -}, { value: 'foobar\nhello\nworld'}); -testVim('blockwise_paste_last_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'l', 'y'); - cm.setCursor(3, 0); - helpers.doKeys('p'); - eq('cut\nand\npaste\nmcue\n an\n pa', cm.getValue()); -}, { value: 'cut\nand\npaste\nme'}); - -testVim('S_visual', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v', 'j', 'S'); - helpers.doKeys(''); - helpers.assertCursorAt(0, 0); - eq('\ncc', cm.getValue()); -}, { value: 'aa\nbb\ncc'}); - -testVim('d_/', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('2', 'd', '/'); - helpers.assertCursorAt(0, 0); - eq('match \n next', cm.getValue()); - cm.openDialog = helpers.fakeOpenDialog('2'); - helpers.doKeys('d', ':'); - // TODO eq(' next', cm.getValue()); -}, { value: 'text match match \n next' }); -testVim('/ and n/N', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 6); - helpers.doKeys('N'); - helpers.assertCursorAt(0, 11); - - cm.setCursor(0, 0); - helpers.doKeys('2', '/'); - helpers.assertCursorAt(1, 6); -}, { value: 'match nope match \n nope Match' }); -testVim('/ and gn selects the appropriate word', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - - // gn should highlight the the current word while it is within a match. - - // gn when cursor is in beginning of match - helpers.doKeys('gn', ''); - helpers.assertCursorAt(0, 15); - - // gn when cursor is at end of match - helpers.doKeys('gn', ''); - helpers.doKeys(''); - helpers.assertCursorAt(0, 15); - - // consecutive gns should extend the selection - helpers.doKeys('gn'); - helpers.assertCursorAt(0, 16); - helpers.doKeys('gn'); - helpers.assertCursorAt(1, 11); - - // we should have selected the second and third "match" - helpers.doKeys('d'); - eq('match nope ', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('/ and gN selects the appropriate word', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - - // gN when cursor is at beginning of match - helpers.doKeys('gN', ''); - helpers.assertCursorAt(0, 11); - - // gN when cursor is at end of match - helpers.doKeys('e', 'gN', ''); - helpers.assertCursorAt(0, 11); - - // consecutive gNs should extend the selection - helpers.doKeys('gN'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('gN'); - helpers.assertCursorAt(0, 0); - - // we should have selected the first and second "match" - helpers.doKeys('d'); - eq(' \n nope Match', cm.getValue()); -}, { value: 'match nope match \n nope Match' }) -testVim('/ and gn with an associated operator', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - - helpers.doKeys('c', 'gn', 'changed', ''); - - // change the current match. - eq('match nope changed \n nope Match', cm.getValue()); - - // change the next match. - helpers.doKeys('.'); - eq('match nope changed \n nope changed', cm.getValue()); - - // change the final match. - helpers.doKeys('.'); - eq('changed nope changed \n nope changed', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('/ and gN with an associated operator', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - - helpers.doKeys('c', 'gN', 'changed', ''); - - // change the current match. - eq('match nope changed \n nope Match', cm.getValue()); - - // change the next match. - helpers.doKeys('.'); - eq('changed nope changed \n nope Match', cm.getValue()); - - // change the final match. - helpers.doKeys('.'); - eq('changed nope changed \n nope changed', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('/_case', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('Match'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 6); -}, { value: 'match nope match \n nope Match' }); -testVim('/_2_pcre', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', true); - cm.openDialog = helpers.fakeOpenDialog('(word){2}'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 9); - helpers.doKeys('n'); - helpers.assertCursorAt(2, 1); -}, { value: 'word\n another wordword\n wordwordword\n' }); -testVim('/_2_nopcre', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', false); - cm.openDialog = helpers.fakeOpenDialog('\\(word\\)\\{2}'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 9); - helpers.doKeys('n'); - helpers.assertCursorAt(2, 1); -}, { value: 'word\n another wordword\n wordwordword\n' }); -testVim('/_nongreedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('aa'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('?_nongreedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('aa'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 1); -}, { value: 'aaa aa \n a aa'}); -testVim('/_greedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a+'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('?_greedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a+'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('/_greedy_0_or_more', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a*'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 5); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 0); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa\n aa'}); -testVim('?_greedy_0_or_more', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a*'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 5); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa\n aa'}); -testVim('? and n/N', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 6); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('N'); - helpers.assertCursorAt(1, 6); - - cm.setCursor(0, 0); - helpers.doKeys('2', '?'); - helpers.assertCursorAt(0, 11); -}, { value: 'match nope match \n nope Match' }); -testVim('? and gn selects the appropriate word', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?', 'n'); - helpers.assertCursorAt(0, 11); - - // gn should highlight the the current word while it is within a match. - - // gn when cursor is in beginning of match - helpers.doKeys('gn', ''); - helpers.assertCursorAt(0, 11); - - // gn when cursor is at end of match - helpers.doKeys('e', 'gn', ''); - helpers.assertCursorAt(0, 11); - - // consecutive gns should extend the selection - helpers.doKeys('gn'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('gn'); - helpers.assertCursorAt(0, 0); - - // we should have selected the first and second "match" - helpers.doKeys('d'); - eq(' \n nope Match', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('? and gN selects the appropriate word', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?', 'n'); - helpers.assertCursorAt(0, 11); - - // gN when cursor is at beginning of match - helpers.doKeys('gN', ''); - helpers.assertCursorAt(0, 15); - - // gN when cursor is at end of match - helpers.doKeys('gN', ''); - helpers.assertCursorAt(0, 15); - - // consecutive gNs should extend the selection - helpers.doKeys('gN'); - helpers.assertCursorAt(0, 16); - helpers.doKeys('gN'); - helpers.assertCursorAt(1, 11); - - // we should have selected the second and third "match" - helpers.doKeys('d'); - eq('match nope ', cm.getValue()); -}, { value: 'match nope match \n nope Match' }) -testVim('? and gn with an associated operator', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?', 'n'); - helpers.assertCursorAt(0, 11); - - helpers.doKeys('c', 'gn', 'changed', ''); - - // change the current match. - eq('match nope changed \n nope Match', cm.getValue()); - - // change the next match. - helpers.doKeys('.'); - eq('changed nope changed \n nope Match', cm.getValue()); - - // change the final match. - helpers.doKeys('.'); - eq('changed nope changed \n nope changed', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('? and gN with an associated operator', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?', 'n'); - helpers.assertCursorAt(0, 11); - - helpers.doKeys('c', 'gN', 'changed', ''); - - // change the current match. - eq('match nope changed \n nope Match', cm.getValue()); - - // change the next match. - helpers.doKeys('.'); - eq('match nope changed \n nope changed', cm.getValue()); - - // change the final match. - helpers.doKeys('.'); - eq('changed nope changed \n nope changed', cm.getValue()); -}, { value: 'match nope match \n nope Match' }); -testVim('*', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 22); - - cm.setCursor(0, 9); - helpers.doKeys('2', '*'); - helpers.assertCursorAt(1, 8); -}, { value: 'nomatch match nomatch match \nnomatch Match' }); -testVim('*_no_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 0); -}, { value: ' \n match \n' }); -testVim('*_symbol', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('*'); - helpers.assertCursorAt(1, 0); -}, { value: ' /}\n/} match \n' }); -testVim('#', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('#'); - helpers.assertCursorAt(1, 8); - - cm.setCursor(0, 9); - helpers.doKeys('2', '#'); - helpers.assertCursorAt(0, 22); -}, { value: 'nomatch match nomatch match \nnomatch Match' }); -testVim('*_seek', function(cm, vim, helpers) { - // Should skip over space and symbols. - cm.setCursor(0, 3); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 22); -}, { value: ' := match nomatch match \nnomatch Match' }); -testVim('#', function(cm, vim, helpers) { - // Should skip over space and symbols. - cm.setCursor(0, 3); - helpers.doKeys('#'); - helpers.assertCursorAt(1, 8); -}, { value: ' := match nomatch match \nnomatch Match' }); -testVim('g*', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('g', '*'); - helpers.assertCursorAt(0, 18); - cm.setCursor(0, 8); - helpers.doKeys('3', 'g', '*'); - helpers.assertCursorAt(1, 8); -}, { value: 'matches match alsoMatch\nmatchme matching' }); -testVim('g#', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('g', '#'); - helpers.assertCursorAt(0, 0); - cm.setCursor(0, 8); - helpers.doKeys('3', 'g', '#'); - helpers.assertCursorAt(1, 0); -}, { value: 'matches match alsoMatch\nmatchme matching' }); -testVim('macro_insert', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '0', 'i'); - helpers.doKeys('foo') - helpers.doKeys(''); - helpers.doKeys('q', '@', 'a'); - eq('foofoo', cm.getValue()); -}, { value: ''}); -testVim('macro_insert_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '$', 'a'); - helpers.doKeys('larry.') - helpers.doKeys(''); - helpers.doKeys('a'); - helpers.doKeys('curly.') - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('a'); - helpers.doKeys('moe.') - helpers.doKeys(''); - helpers.doKeys('@', 'a'); - // At this point, the most recent edit should be the 2nd insert change - // inside the macro, i.e. "curly.". - helpers.doKeys('.'); - eq('larry.curly.moe.larry.curly.curly.', cm.getValue()); -}, { value: ''}); -testVim('macro_space', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', ''); - helpers.assertCursorAt(0, 2); - helpers.doKeys('q', 'a', '', '', 'q'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0, 6); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0, 8); -}, { value: 'one line of text.'}); -testVim('macro_t_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 't', 'e', 'q'); - helpers.assertCursorAt(0, 1); - helpers.doKeys('l', '@', 'a'); - helpers.assertCursorAt(0, 6); - helpers.doKeys('l', ';'); - helpers.assertCursorAt(0, 12); -}, { value: 'one line of text.'}); -testVim('macro_f_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'b', 'f', 'e', 'q'); - helpers.assertCursorAt(0, 2); - helpers.doKeys('@', 'b'); - helpers.assertCursorAt(0, 7); - helpers.doKeys(';'); - helpers.assertCursorAt(0, 13); -}, { value: 'one line of text.'}); -testVim('macro_slash_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'c'); - cm.openDialog = helpers.fakeOpenDialog('e'); - helpers.doKeys('/', 'q'); - helpers.assertCursorAt(0, 2); - helpers.doKeys('@', 'c'); - helpers.assertCursorAt(0, 7); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 13); -}, { value: 'one line of text.'}); -testVim('macro_multislash_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'd'); - cm.openDialog = helpers.fakeOpenDialog('e'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('t'); - helpers.doKeys('/', 'q'); - helpers.assertCursorAt(0, 12); - helpers.doKeys('@', 'd'); - helpers.assertCursorAt(0, 15); -}, { value: 'one line of text to rule them all.'}); -testVim('macro_last_ex_command_register', function (cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('s/a/b'); - helpers.doKeys('2', '@', ':'); - eq('bbbaa', cm.getValue()); - helpers.assertCursorAt(0, 2); -}, { value: 'aaaaa'}); -testVim('macro_last_run_macro', function (cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'C', 'a', '', 'q'); - helpers.doKeys('q', 'b', 'C', 'b', '', 'q'); - helpers.doKeys('@', 'a'); - helpers.doKeys('d', 'd'); - helpers.doKeys('@', '@'); - eq('a', cm.getValue()); -}, { value: ''}); -testVim('macro_parens', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'z', 'i'); - helpers.doKeys('(') - helpers.doKeys(''); - helpers.doKeys('e', 'a'); - helpers.doKeys(')') - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('w', '@', 'z'); - helpers.doKeys('w', '@', 'z'); - eq('(see) (spot) (run)', cm.getValue()); -}, { value: 'see spot run'}); -testVim('macro_overwrite', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'z', '0', 'i'); - helpers.doKeys('I ') - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('e'); - // Now replace the macro with something else. - helpers.doKeys('q', 'z', 'a'); - helpers.doKeys('.') - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('e', '@', 'z'); - helpers.doKeys('e', '@', 'z'); - eq('I see. spot. run.', cm.getValue()); -}, { value: 'see spot run'}); -testVim('macro_search_f', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'f', ' '); - helpers.assertCursorAt(0,3); - helpers.doKeys('q', '0'); - helpers.assertCursorAt(0,0); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0,3); -}, { value: 'The quick brown fox jumped over the lazy dog.'}); -testVim('macro_search_2f', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '2', 'f', ' '); - helpers.assertCursorAt(0,9); - helpers.doKeys('q', '0'); - helpers.assertCursorAt(0,0); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0,9); -}, { value: 'The quick brown fox jumped over the lazy dog.'}); -testVim('macro_yank_tick', function(cm, vim, helpers) { - cm.setCursor(0, 0); - // Start recording a macro into the \' register. - helpers.doKeys('q', '\''); - helpers.doKeys('y', '', '', '', '', 'p'); - helpers.assertCursorAt(0,4); - eq('the tex parrot', cm.getValue()); -}, { value: 'the ex parrot'}); -testVim('yank_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'b', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo/.test(text)); - is(/b\s+bar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', 'l', 'j', '"', 'a', 'y'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+oo\nar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_line_to_line_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'A', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_word_to_word_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - helpers.doKeys('j', '"', 'A', 'y', 'w'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foobar/.test(text)); - is(/"\s+foobar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_line_to_word_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - helpers.doKeys('j', '"', 'A', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_word_to_line_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'A', 'y', 'w'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('black_hole_register', function(cm,vim,helpers) { - helpers.doKeys('g', 'g', 'y', 'G'); - var registersText; - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - registersText = text; - }); - helpers.doKeys(':'); - helpers.doKeys('"', '_', 'd', 'G'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - eq(registersText, text, 'One or more registers were modified'); - }); - helpers.doKeys(':'); - helpers.doKeys('"', '_', 'p'); - eq('', cm.getValue()); -}, { value: 'foo\nbar'}); -testVim('macro_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'i'); - helpers.doKeys('gangnam') - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('q', 'b', 'o'); - helpers.doKeys('style') - helpers.doKeys(''); - helpers.doKeys('q'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+i/.test(text)); - is(/b\s+o/.test(text)); - }); - helpers.doKeys(':'); -}, { value: ''}); -testVim('._register', function(cm,vim,helpers) { - cm.setCursor(0,0); - helpers.doKeys('i'); - helpers.doKeys('foo') - helpers.doKeys(''); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/\.\s+foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim(':_register', function(cm,vim,helpers) { - helpers.doEx('bar'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/:\s+bar/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_register_escape', function(cm, vim, helpers) { - // Check that the register is restored if the user escapes rather than confirms. - cm.openDialog = helpers.fakeOpenDialog('waldo'); - helpers.doKeys('/'); - var onKeyDown; - var onKeyUp; - var KEYCODES = { - f: 70, - o: 79, - Esc: 27 - }; - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - onKeyUp = options.onKeyUp; - }; - var close = function() {}; - helpers.doKeys('/'); - // Fake some keyboard events coming in. - onKeyDown({keyCode: KEYCODES.f}, '', close); - onKeyUp({keyCode: KEYCODES.f}, '', close); - onKeyDown({keyCode: KEYCODES.o}, 'f', close); - onKeyUp({keyCode: KEYCODES.o}, 'f', close); - onKeyDown({keyCode: KEYCODES.o}, 'fo', close); - onKeyUp({keyCode: KEYCODES.o}, 'fo', close); - onKeyDown({keyCode: KEYCODES.Esc}, 'foo', close); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/waldo/.test(text)); - is(!/foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_register', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('foo'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/\/\s+foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_history', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('this'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('checks'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('search'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('history'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('checks'); - helpers.doKeys('/'); - var onKeyDown; - var onKeyUp; - var query = ''; - var keyCodes = { - Up: 38, - Down: 40 - }; - cm.openDialog = function(template, callback, options) { - onKeyUp = options.onKeyUp; - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') query = newVal; - } - helpers.doKeys('/'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'checks'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'history'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'search'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'this'); - onKeyDown({keyCode: keyCodes.Down}, query, close); - onKeyUp({keyCode: keyCodes.Down}, query, close); - eq(query, 'search'); -}, {value: ''}); -testVim('exCommand_history', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('registers'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('sort'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('map'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('invalid'); - helpers.doKeys(':'); - var onKeyDown; - var onKeyUp; - var input = ''; - var keyCodes = { - Up: 38, - Down: 40, - s: 115 - }; - cm.openDialog = function(template, callback, options) { - onKeyUp = options.onKeyUp; - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') input = newVal; - } - helpers.doKeys(':'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'invalid'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'map'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'sort'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'registers'); - onKeyDown({keyCode: keyCodes.s}, '', close); - input = 's'; - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'sort'); -}, {value: ''}); -testVim('search_clear', function(cm, vim, helpers) { - var onKeyDown; - var input = ''; - var keyCodes = { - Ctrl: 17, - u: 85 - }; - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') input = newVal; - } - helpers.doKeys('/'); - input = 'foo'; - onKeyDown({keyCode: keyCodes.Ctrl}, input, close); - onKeyDown({keyCode: keyCodes.u, ctrlKey: true}, input, close); - eq(input, ''); -}); -testVim('exCommand_clear', function(cm, vim, helpers) { - var onKeyDown; - var input = ''; - var keyCodes = { - Ctrl: 17, - u: 85 - }; - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') input = newVal; - } - helpers.doKeys(':'); - input = 'foo'; - onKeyDown({keyCode: keyCodes.Ctrl}, input, close); - onKeyDown({keyCode: keyCodes.u, ctrlKey: true}, input, close); - eq(input, ''); -}); -testVim('.', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('2', 'd', 'w'); - helpers.doKeys('.'); - eq('5 6', cm.getValue()); -}, { value: '1 2 3 4 5 6'}); -testVim('._repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('2', 'd', 'w'); - helpers.doKeys('3', '.'); - eq('6', cm.getValue()); -}, { value: '1 2 3 4 5 6'}); -testVim('._insert', function(cm, vim, helpers) { - helpers.doKeys('i'); - helpers.doKeys('test') - helpers.doKeys(''); - helpers.doKeys('.'); - eq('testestt', cm.getValue()); - helpers.assertCursorAt(0, 6); - helpers.doKeys('O'); - helpers.doKeys('xyz') - helpers.doInsertModeKeys('Backspace'); - helpers.doInsertModeKeys('Down'); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('xy\nxy\ntestestt', cm.getValue()); - helpers.assertCursorAt(1, 1); -}, { value: ''}); -testVim('._insert_repeat', function(cm, vim, helpers) { - helpers.doKeys('i'); - helpers.doKeys('test') - cm.setCursor(0, 4); - helpers.doKeys(''); - helpers.doKeys('2', '.'); - eq('testesttestt', cm.getValue()); - helpers.assertCursorAt(0, 10); -}, { value: ''}); -testVim('._repeat_insert', function(cm, vim, helpers) { - helpers.doKeys('3', 'i'); - helpers.doKeys('te') - cm.setCursor(0, 2); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('tetettetetee', cm.getValue()); - helpers.assertCursorAt(0, 10); -}, { value: ''}); -testVim('._insert_o', function(cm, vim, helpers) { - helpers.doKeys('o'); - helpers.doKeys('z') - cm.setCursor(1, 1); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('\nz\nz', cm.getValue()); - helpers.assertCursorAt(2, 0); -}, { value: ''}); -testVim('._insert_o_repeat', function(cm, vim, helpers) { - helpers.doKeys('o'); - helpers.doKeys('z') - helpers.doKeys(''); - cm.setCursor(1, 0); - helpers.doKeys('2', '.'); - eq('\nz\nz\nz', cm.getValue()); - helpers.assertCursorAt(3, 0); -}, { value: ''}); -testVim('._insert_o_indent', function(cm, vim, helpers) { - helpers.doKeys('o'); - helpers.doKeys('z') - helpers.doKeys(''); - cm.setCursor(1, 2); - helpers.doKeys('.'); - eq('{\n z\n z', cm.getValue()); - helpers.assertCursorAt(2, 2); -}, { value: '{'}); -testVim('._insert_cw', function(cm, vim, helpers) { - helpers.doKeys('c', 'w'); - helpers.doKeys('test') - helpers.doKeys(''); - cm.setCursor(0, 3); - helpers.doKeys('2', 'l'); - helpers.doKeys('.'); - eq('test test word3', cm.getValue()); - helpers.assertCursorAt(0, 8); -}, { value: 'word1 word2 word3' }); -testVim('._insert_cw_repeat', function(cm, vim, helpers) { - // For some reason, repeat cw in desktop VIM will does not repeat insert mode - // changes. Will conform to that behavior. - helpers.doKeys('c', 'w'); - helpers.doKeys('test'); - helpers.doKeys(''); - cm.setCursor(0, 4); - helpers.doKeys('l'); - helpers.doKeys('2', '.'); - eq('test test', cm.getValue()); - helpers.assertCursorAt(0, 8); -}, { value: 'word1 word2 word3' }); -testVim('._delete', function(cm, vim, helpers) { - cm.setCursor(0, 5); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Backspace'); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('zace', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'zabcde'}); -testVim('._delete_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Backspace'); - helpers.doKeys(''); - helpers.doKeys('2', '.'); - eq('zzce', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'zzabcde'}); -testVim('._visual_>', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('V', 'j', '>'); - cm.setCursor(2, 0) - helpers.doKeys('.'); - eq(' 1\n 2\n 3\n 4', cm.getValue()); - helpers.assertCursorAt(2, 2); -}, { value: '1\n2\n3\n4'}); -testVim('._replace_repeat', function(cm, vim, helpers) { - helpers.doKeys('R'); - cm.replaceRange('123', cm.getCursor(), offsetCursor(cm.getCursor(), 0, 3)); - cm.setCursor(0, 3); - helpers.doKeys(''); - helpers.doKeys('2', '.'); - eq('12123123\nabcdefg', cm.getValue()); - helpers.assertCursorAt(0, 7); - cm.setCursor(1, 0); - helpers.doKeys('.'); - eq('12123123\n123123g', cm.getValue()); - helpers.doKeys('l', '"', '.', 'p'); - eq('12123123\n123123g123', cm.getValue()); -}, { value: 'abcdef\nabcdefg'}); -testVim('f;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(9, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('F;', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('F', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(2, cm.getCursor().ch); -}, { value: '01x3xx6x8x'}); -testVim('t;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(8, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('T;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(2, cm.getCursor().ch); -}, { value: '0xx3xx678x'}); -testVim('f,', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('f', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(2, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('F,', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('F', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(9, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('t,', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('t', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(3, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('T,', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('T', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(8, cm.getCursor().ch); -}, { value: '01x3xx67xx'}); -testVim('fd,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('d', ';'); - eq('56789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('d', ','); - eq('01239', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fd,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('d', ';'); - eq('01239', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('d', ','); - eq('56789', cm.getValue()); -}, { value: '0123456789'}); -testVim('td,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('d', ';'); - eq('456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('d', ','); - eq('012349', cm.getValue()); -}, { value: '0123456789'}); -testVim('Td,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('d', ';'); - eq('012349', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('d', ','); - eq('456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('fc,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('c', ';', ''); - eq('56789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('c', ','); - eq('01239', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fc,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('c', ';', ''); - eq('01239', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('c', ','); - eq('56789', cm.getValue()); -}, { value: '0123456789'}); -testVim('tc,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('c', ';', ''); - eq('456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('c', ','); - eq('012349', cm.getValue()); -}, { value: '0123456789'}); -testVim('Tc,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('c', ';', ''); - eq('012349', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('c', ','); - eq('456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('fy,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('y', ';', 'P'); - eq('012340123456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('y', ',', 'P'); - eq('012345678456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fy,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('y', ';', 'p'); - eq('012345678945678', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('y', ',', 'P'); - eq('012340123456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('ty,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('y', ';', 'P'); - eq('01230123456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('y', ',', 'p'); - eq('01234567895678', cm.getValue()); -}, { value: '0123456789'}); -testVim('Ty,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('y', ';', 'p'); - eq('01234567895678', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('y', ',', 'P'); - eq('01230123456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('HML', function(cm, vim, helpers) { - var lines = 35; - var textHeight = cm.defaultTextHeight(); - cm.setSize(600, lines*textHeight); - cm.setCursor(120, 0); - helpers.doKeys('H'); - helpers.assertCursorAt(86, 2); - helpers.doKeys('L'); - helpers.assertCursorAt(120, 4); - helpers.doKeys('M'); - helpers.assertCursorAt(103,4); -}, { value: (function(){ - var lines = new Array(100); - var upper = ' xx\n'; - var lower = ' xx\n'; - upper = lines.join(upper); - lower = lines.join(lower); - return upper + lower; -})()}); - -var zVals = []; -forEach(['zb','zz','zt','z-','z.','z'], function(e, idx){ - var lineNum = 250; - var lines = 35; - testVim(e, function(cm, vim, helpers) { - var k1 = e[0]; - var k2 = e.substring(1); - var textHeight = cm.defaultTextHeight(); - cm.setSize(600, lines*textHeight); - cm.setCursor(lineNum, 0); - helpers.doKeys(k1, k2); - zVals[idx] = cm.getScrollInfo().top; - }, { value: (function(){ - return new Array(500).join('\n'); - })()}); -}); -testVim('zb_to_bottom', function(cm, vim, helpers){ - var lineNum = 250; - cm.setSize(600, 35*cm.defaultTextHeight()); - cm.setCursor(lineNum, 0); - helpers.doKeys('z', 'b'); - var scrollInfo = cm.getScrollInfo(); - eq(scrollInfo.top + scrollInfo.clientHeight, cm.charCoords(Pos(lineNum, 0), 'local').bottom); -}, { value: (function(){ - return new Array(500).join('\n'); -})()}); -testVim('zt_to_top', function(cm, vim, helpers){ - var lineNum = 250; - cm.setSize(600, 35*cm.defaultTextHeight()); - cm.setCursor(lineNum, 0); - helpers.doKeys('z', 't'); - eq(cm.getScrollInfo().top, cm.charCoords(Pos(lineNum, 0), 'local').top); -}, { value: (function(){ - return new Array(500).join('\n'); -})()}); -testVim('zb', function(cm, vim, helpers){ - eq(zVals[2], zVals[5]); -}); - -var moveTillCharacterSandbox = - 'The quick brown fox \n'; -testVim('moveTillCharacter', function(cm, vim, helpers){ - cm.setCursor(0, 0); - // Search for the 'q'. - cm.openDialog = helpers.fakeOpenDialog('q'); - helpers.doKeys('/'); - eq(4, cm.getCursor().ch); - // Jump to just before the first o in the list. - helpers.doKeys('t'); - helpers.doKeys('o'); - eq('The quick brown fox \n', cm.getValue()); - // Delete that one character. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('o'); - eq('The quick bown fox \n', cm.getValue()); - // Delete everything until the next 'o'. - helpers.doKeys('.'); - eq('The quick box \n', cm.getValue()); - // An unmatched character should have no effect. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('q'); - eq('The quick box \n', cm.getValue()); - // Matches should only be possible on single lines. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('z'); - eq('The quick box \n', cm.getValue()); - // After all that, the search for 'q' should still be active, so the 'N' command - // can run it again in reverse. Use that to delete everything back to the 'q'. - helpers.doKeys('d'); - helpers.doKeys('N'); - eq('The ox \n', cm.getValue()); - eq(4, cm.getCursor().ch); -}, { value: moveTillCharacterSandbox}); -testVim('searchForPipe', function(cm, vim, helpers){ - CodeMirror.Vim.setOption('pcre', false); - cm.setCursor(0, 0); - // Search for the '|'. - cm.openDialog = helpers.fakeOpenDialog('|'); - helpers.doKeys('/'); - eq(4, cm.getCursor().ch); -}, { value: 'this|that'}); - - -var scrollMotionSandbox = - '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'; -testVim('scrollMotion', function(cm, vim, helpers){ - var prevCursor, prevScrollInfo; - cm.setCursor(0, 0); - // ctrl-y at the top of the file should have no effect. - helpers.doKeys(''); - eq(0, cm.getCursor().line); - prevScrollInfo = cm.getScrollInfo(); - helpers.doKeys(''); - eq(1, cm.getCursor().line); - is(prevScrollInfo.top < cm.getScrollInfo().top); - // Jump to the end of the sandbox. - cm.setCursor(1000, 0); - prevCursor = cm.getCursor(); - // ctrl-e at the bottom of the file should have no effect. - helpers.doKeys(''); - eq(prevCursor.line, cm.getCursor().line); - prevScrollInfo = cm.getScrollInfo(); - helpers.doKeys(''); - eq(prevCursor.line - 1, cm.getCursor().line, "Y"); - is(prevScrollInfo.top > cm.getScrollInfo().top); -}, { value: scrollMotionSandbox}); - -var squareBracketMotionSandbox = ''+ - '({\n'+//0 - ' ({\n'+//11 - ' /*comment {\n'+//2 - ' */(\n'+//3 - '#else \n'+//4 - ' /* )\n'+//5 - '#if }\n'+//6 - ' )}*/\n'+//7 - ')}\n'+//8 - '{}\n'+//9 - '#else {{\n'+//10 - '{}\n'+//11 - '}\n'+//12 - '{\n'+//13 - '#endif\n'+//14 - '}\n'+//15 - '}\n'+//16 - '#else';//17 -testVim('[[, ]]', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(']', ']'); - helpers.assertCursorAt(9,0); - helpers.doKeys('2', ']', ']'); - helpers.assertCursorAt(13,0); - helpers.doKeys(']', ']'); - helpers.assertCursorAt(17,0); - helpers.doKeys('[', '['); - helpers.assertCursorAt(13,0); - helpers.doKeys('2', '[', '['); - helpers.assertCursorAt(9,0); - helpers.doKeys('[', '['); - helpers.assertCursorAt(0,0); -}, { value: squareBracketMotionSandbox}); -testVim('[], ][', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(']', '['); - helpers.assertCursorAt(12,0); - helpers.doKeys('2', ']', '['); - helpers.assertCursorAt(16,0); - helpers.doKeys(']', '['); - helpers.assertCursorAt(17,0); - helpers.doKeys('[', ']'); - helpers.assertCursorAt(16,0); - helpers.doKeys('2', '[', ']'); - helpers.assertCursorAt(12,0); - helpers.doKeys('[', ']'); - helpers.assertCursorAt(0,0); -}, { value: squareBracketMotionSandbox}); -testVim('[{, ]}', function(cm, vim, helpers) { - cm.setCursor(4, 10); - helpers.doKeys('[', '{'); - helpers.assertCursorAt(2,12); - helpers.doKeys('2', '[', '{'); - helpers.assertCursorAt(0,1); - cm.setCursor(4, 10); - helpers.doKeys(']', '}'); - helpers.assertCursorAt(6,11); - helpers.doKeys('2', ']', '}'); - helpers.assertCursorAt(8,1); - cm.setCursor(0,1); - helpers.doKeys(']', '}'); - helpers.assertCursorAt(8,1); - helpers.doKeys('[', '{'); - helpers.assertCursorAt(0,1); -}, { value: squareBracketMotionSandbox}); -testVim('[(, ])', function(cm, vim, helpers) { - cm.setCursor(4, 10); - helpers.doKeys('[', '('); - helpers.assertCursorAt(3,14); - helpers.doKeys('2', '[', '('); - helpers.assertCursorAt(0,0); - cm.setCursor(4, 10); - helpers.doKeys(']', ')'); - helpers.assertCursorAt(5,11); - helpers.doKeys('2', ']', ')'); - helpers.assertCursorAt(8,0); - helpers.doKeys('[', '('); - helpers.assertCursorAt(0,0); - helpers.doKeys(']', ')'); - helpers.assertCursorAt(8,0); -}, { value: squareBracketMotionSandbox}); -testVim('[*, ]*, [/, ]/', function(cm, vim, helpers) { - forEach(['*', '/'], function(key){ - cm.setCursor(7, 0); - helpers.doKeys('2', '[', key); - helpers.assertCursorAt(2,2); - helpers.doKeys('2', ']', key); - helpers.assertCursorAt(7,5); - }); -}, { value: squareBracketMotionSandbox}); -testVim('[#, ]#', function(cm, vim, helpers) { - cm.setCursor(10, 3); - helpers.doKeys('2', '[', '#'); - helpers.assertCursorAt(4,0); - helpers.doKeys('5', ']', '#'); - helpers.assertCursorAt(17,0); - cm.setCursor(10, 3); - helpers.doKeys(']', '#'); - helpers.assertCursorAt(14,0); -}, { value: squareBracketMotionSandbox}); -testVim('[m, ]m, [M, ]M', function(cm, vim, helpers) { - cm.setCursor(11, 0); - helpers.doKeys('[', 'm'); - helpers.assertCursorAt(10,7); - helpers.doKeys('4', '[', 'm'); - helpers.assertCursorAt(1,3); - helpers.doKeys('5', ']', 'm'); - helpers.assertCursorAt(11,0); - helpers.doKeys('[', 'M'); - helpers.assertCursorAt(9,1); - helpers.doKeys('3', ']', 'M'); - helpers.assertCursorAt(15,0); - helpers.doKeys('5', '[', 'M'); - helpers.assertCursorAt(7,3); -}, { value: squareBracketMotionSandbox}); - -testVim('i_indent_right', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('i', ''); - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, 5); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); -testVim('i_indent_left', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('i', ''); - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); - -// Ex mode tests -testVim('ex_go_to_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('4'); - helpers.assertCursorAt(3, 0); -}, { value: 'a\nb\nc\nd\ne\n'}); -testVim('ex_go_to_mark', function(cm, vim, helpers) { - cm.setCursor(3, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(0, 0); - helpers.doEx('\'a'); - helpers.assertCursorAt(3, 0); -}, { value: 'a\nb\nc\nd\ne\n'}); -testVim('ex_go_to_line_offset', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('+3'); - helpers.assertCursorAt(3, 0); - helpers.doEx('-1'); - helpers.assertCursorAt(2, 0); - helpers.doEx('.2'); - helpers.assertCursorAt(4, 0); - helpers.doEx('.-3'); - helpers.assertCursorAt(1, 0); -}, { value: 'a\nb\nc\nd\ne\n'}); -testVim('ex_go_to_mark_offset', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(0, 0); - helpers.doEx('\'a1'); - helpers.assertCursorAt(3, 0); - helpers.doEx('\'a-1'); - helpers.assertCursorAt(1, 0); - helpers.doEx('\'a+2'); - helpers.assertCursorAt(4, 0); -}, { value: 'a\nb\nc\nd\ne\n'}); -testVim('ex_write', function(cm, vim, helpers) { - var tmp = CodeMirror.commands.save; - var written; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - // Test that w, wr, wri ... write all trigger :write. - var command = 'write'; - for (var i = 1; i < command.length; i++) { - written = false; - actualCm = null; - helpers.doEx(command.substring(0, i)); - eq(written, true); - eq(actualCm, cm); - } - CodeMirror.commands.save = tmp; -}); -testVim('ex_sort', function(cm, vim, helpers) { - helpers.doEx('sort'); - eq('Z\na\nb\nc\nd', cm.getValue()); -}, { value: 'b\nZ\nd\nc\na'}); -testVim('ex_sort_reverse', function(cm, vim, helpers) { - helpers.doEx('sort!'); - eq('d\nc\nb\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_range', function(cm, vim, helpers) { - helpers.doEx('2,3sort'); - eq('b\nc\nd\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_oneline', function(cm, vim, helpers) { - helpers.doEx('2sort'); - // Expect no change. - eq('b\nd\nc\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_ignoreCase', function(cm, vim, helpers) { - helpers.doEx('sort i'); - eq('a\nb\nc\nd\nZ', cm.getValue()); -}, { value: 'b\nZ\nd\nc\na'}); -testVim('ex_sort_unique', function(cm, vim, helpers) { - helpers.doEx('sort u'); - eq('Z\na\nb\nc\nd', cm.getValue()); -}, { value: 'b\nZ\na\na\nd\na\nc\na'}); -testVim('ex_sort_decimal', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('d3\n s5\n6\n.9', cm.getValue()); -}, { value: '6\nd3\n s5\n.9'}); -testVim('ex_sort_decimal_negative', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('z-9\nd3\n s5\n6\n.9', cm.getValue()); -}, { value: '6\nd3\n s5\n.9\nz-9'}); -testVim('ex_sort_decimal_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! d'); - eq('.9\n6\n s5\nd3', cm.getValue()); -}, { value: '6\nd3\n s5\n.9'}); -testVim('ex_sort_hex', function(cm, vim, helpers) { - helpers.doEx('sort x'); - eq(' s5\n6\n.9\n&0xB\nd3', cm.getValue()); -}, { value: '6\nd3\n s5\n&0xB\n.9'}); -testVim('ex_sort_octal', function(cm, vim, helpers) { - helpers.doEx('sort o'); - eq('.9\n.8\nd3\n s5\n6', cm.getValue()); -}, { value: '6\nd3\n s5\n.9\n.8'}); -testVim('ex_sort_decimal_mixed', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('z\ny\nc1\nb2\na3', cm.getValue()); -}, { value: 'a3\nz\nc1\ny\nb2'}); -testVim('ex_sort_decimal_mixed_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! d'); - eq('a3\nb2\nc1\nz\ny', cm.getValue()); -}, { value: 'a3\nz\nc1\ny\nb2'}); -testVim('ex_sort_pattern_alpha', function(cm, vim, helpers) { - helpers.doEx('sort /[a-z]/'); - eq('a3\nb2\nc1\ny\nz', cm.getValue()); -}, { value: 'z\ny\nc1\nb2\na3'}); -testVim('ex_sort_pattern_alpha_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! /[a-z]/'); - eq('z\ny\nc1\nb2\na3', cm.getValue()); -}, { value: 'z\ny\nc1\nb2\na3'}); -testVim('ex_sort_pattern_alpha_ignoreCase', function(cm, vim, helpers) { - helpers.doEx('sort i/[a-z]/'); - eq('a3\nb2\nC1\nY\nz', cm.getValue()); -}, { value: 'z\nY\nC1\nb2\na3'}); -testVim('ex_sort_pattern_alpha_longer', function(cm, vim, helpers) { - helpers.doEx('sort /[a-z]+/'); - eq('a\naa\nab\nade\nadele\nadelle\nadriana\nalex\nalexandra\nb\nc\ny\nz', cm.getValue()); -}, { value: 'z\nab\naa\nade\nadelle\nalexandra\nalex\nadriana\nadele\ny\nc\nb\na'}); -testVim('ex_sort_pattern_alpha_only', function(cm, vim, helpers) { - helpers.doEx('sort /^[a-z]$/'); - eq('z1\ny2\na3\nb\nc', cm.getValue()); -}, { value: 'z1\ny2\na3\nc\nb'}); -testVim('ex_sort_pattern_alpha_only_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! /^[a-z]$/'); - eq('c\nb\nz1\ny2\na3', cm.getValue()); -}, { value: 'z1\ny2\na3\nc\nb'}); -testVim('ex_sort_pattern_alpha_num', function(cm, vim, helpers) { - helpers.doEx('sort /[a-z][0-9]/'); - eq('c\nb\na3\ny2\nz1', cm.getValue()); -}, { value: 'z1\ny2\na3\nc\nb'}); -// test for :global command -testVim('ex_global', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('g/one/s//two'); - eq('two two\n two two\n two two', cm.getValue()); - helpers.doEx('1,2g/two/s//one'); - eq('one one\n one one\n two two', cm.getValue()); -}, {value: 'one one\n one one\n one one'}); -testVim('ex_global_confirm', function(cm, vim, helpers) { - cm.setCursor(0, 0); - var onKeyDown; - var openDialogSave = cm.openDialog; - var KEYCODES = { - a: 65, - n: 78, - q: 81, - y: 89 - }; - // Intercept the ex command, 'global' - cm.openDialog = function(template, callback, options) { - // Intercept the prompt for the embedded ex command, 'substitute' - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - }; - callback('g/one/s//two/gc'); - }; - helpers.doKeys(':'); - var close = function() {}; - onKeyDown({keyCode: KEYCODES.n}, '', close); - onKeyDown({keyCode: KEYCODES.y}, '', close); - onKeyDown({keyCode: KEYCODES.a}, '', close); - onKeyDown({keyCode: KEYCODES.q}, '', close); - onKeyDown({keyCode: KEYCODES.y}, '', close); - eq('one two\n two two\n one one\n two one\n one one', cm.getValue()); -}, {value: 'one one\n one one\n one one\n one one\n one one'}); -// Basic substitute tests. -testVim('ex_substitute_same_line', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('s/one/two/g'); - eq('one one\n two two', cm.getValue()); -}, { value: 'one one\n one one'}); -testVim('ex_substitute_alternate_separator', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('s#o/e#two#g'); - eq('o/e o/e\n two two', cm.getValue()); -}, { value: 'o/e o/e\n o/e o/e'}); -testVim('ex_substitute_full_file', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('%s/one/two/g'); - eq('two two\n two two', cm.getValue()); -}, { value: 'one one\n one one'}); -testVim('ex_substitute_input_range', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('1,3s/\\d/0/g'); - eq('0\n0\n0\n4', cm.getValue()); -}, { value: '1\n2\n3\n4' }); -testVim('ex_substitute_range_current_to_input', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('.,3s/\\d/0/g'); - eq('1\n0\n0\n4', cm.getValue()); -}, { value: '1\n2\n3\n4' }); -testVim('ex_substitute_range_input_to_current', function(cm, vim, helpers) { - cm.setCursor(3, 0); - helpers.doEx('2,.s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_range_offset', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doEx('-1,+1s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_range_implicit_offset', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('.1,.3s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_to_eof', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doEx('.,$s/\\d/0/g'); - eq('1\n2\n0\n0\n0', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_to_relative_eof', function(cm, vim, helpers) { - cm.setCursor(4, 0); - helpers.doEx('2,$-2s/\\d/0/g'); - eq('1\n0\n0\n4\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_range_mark', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('ma'); - cm.setCursor(0, 0); - helpers.doEx('.,\'as/\\d/0/g'); - eq('0\n0\n0\n4\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_range_mark_offset', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('ma'); - cm.setCursor(0, 0); - helpers.doEx('\'a-1,\'a+1s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_visual_range', function(cm, vim, helpers) { - cm.setCursor(1, 0); - // Set last visual mode selection marks '< and '> at lines 2 and 4 - helpers.doKeys('V', '2', 'j', 'v'); - helpers.doEx('\'<,\'>s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_empty_query', function(cm, vim, helpers) { - // If the query is empty, use last query. - cm.setCursor(1, 0); - cm.openDialog = helpers.fakeOpenDialog('1'); - helpers.doKeys('/'); - helpers.doEx('s//b/g'); - eq('abb ab2 ab3', cm.getValue()); -}, { value: 'a11 a12 a13' }); -testVim('ex_substitute_javascript', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', false); - cm.setCursor(1, 0); - // Throw all the things that javascript likes to treat as special values - // into the replace part. All should be literal (this is VIM). - helpers.doEx('s/\\(\\d+\\)/$$ $\' $` $& \\1/g') - eq('a $$ $\' $` $& 0 b', cm.getValue()); -}, { value: 'a 0 b' }); -testVim('ex_substitute_empty_arguments', function(cm,vim,helpers) { - cm.setCursor(0, 0); - helpers.doEx('s/a/b/g'); - cm.setCursor(1, 0); - helpers.doEx('s'); - eq('b b\nb a', cm.getValue()); -}, {value: 'a a\na a'}); - -// More complex substitute tests that test both pcre and nopcre options. -function testSubstitute(name, options) { - testVim(name + '_pcre', function(cm, vim, helpers) { - cm.setCursor(1, 0); - CodeMirror.Vim.setOption('pcre', true); - helpers.doEx(options.expr); - eq(options.expectedValue, cm.getValue()); - }, options); - // If no noPcreExpr is defined, assume that it's the same as the expr. - var noPcreExpr = options.noPcreExpr ? options.noPcreExpr : options.expr; - testVim(name + '_nopcre', function(cm, vim, helpers) { - cm.setCursor(1, 0); - CodeMirror.Vim.setOption('pcre', false); - helpers.doEx(noPcreExpr); - eq(options.expectedValue, cm.getValue()); - }, options); -} -testSubstitute('ex_substitute_capture', { - value: 'a11 a12 a13', - expectedValue: 'a1111 a1212 a1313', - // $n is a backreference - expr: 's/(\\d+)/$1$1/g', - // \n is a backreference. - noPcreExpr: 's/\\(\\d+\\)/\\1\\1/g'}); -testSubstitute('ex_substitute_capture2', { - value: 'a 0 b', - expectedValue: 'a $00 b', - expr: 's/(\\d+)/$$$1$1/g', - noPcreExpr: 's/\\(\\d+\\)/$\\1\\1/g'}); -testSubstitute('ex_substitute_nocapture', { - value: 'a11 a12 a13', - expectedValue: 'a$1$1 a$1$1 a$1$1', - expr: 's/(\\d+)/$$1$$1/g', - noPcreExpr: 's/\\(\\d+\\)/$1$1/g'}); -testSubstitute('ex_substitute_nocapture2', { - value: 'a 0 b', - expectedValue: 'a $10 b', - expr: 's/(\\d+)/$$1$1/g', - noPcreExpr: 's/\\(\\d+\\)/\\$1\\1/g'}); -testSubstitute('ex_substitute_nocapture', { - value: 'a b c', - expectedValue: 'a $ c', - expr: 's/b/$$/', - noPcreExpr: 's/b/$/'}); -testSubstitute('ex_substitute_slash_regex', { - value: 'one/two \n three/four', - expectedValue: 'one|two \n three|four', - expr: '%s/\\//|'}); -testSubstitute('ex_substitute_pipe_regex', { - value: 'one|two \n three|four', - expectedValue: 'one,two \n three,four', - expr: '%s/\\|/,/', - noPcreExpr: '%s/|/,/'}); -testSubstitute('ex_substitute_or_regex', { - value: 'one|two \n three|four', - expectedValue: 'ana|twa \n thraa|faar', - expr: '%s/o|e|u/a/g', - noPcreExpr: '%s/o\\|e\\|u/a/g'}); -testSubstitute('ex_substitute_or_word_regex', { - value: 'one|two \n three|four', - expectedValue: 'five|five \n three|four', - expr: '%s/(one|two)/five/g', - noPcreExpr: '%s/\\(one\\|two\\)/five/g'}); -testSubstitute('ex_substitute_forward_slash_regex', { - value: 'forward slash \/ was here', - expectedValue: 'forward slash was here', - expr: '%s#\\/##g', - noPcreExpr: '%s#/##g'}); -testVim("ex_substitute_ampersand_pcre", function(cm, vim, helpers) { - cm.setCursor(0, 0); - CodeMirror.Vim.setOption('pcre', true); - helpers.doEx('%s/foo/namespace.&/'); - eq("namespace.foo", cm.getValue()); - }, { value: 'foo' }); -testVim("ex_substitute_ampersand_multiple_pcre", function(cm, vim, helpers) { - cm.setCursor(0, 0); - CodeMirror.Vim.setOption('pcre', true); - helpers.doEx('%s/f.o/namespace.&/'); - eq("namespace.foo\nnamespace.fzo", cm.getValue()); - }, { value: 'foo\nfzo' }); -testVim("ex_escaped_ampersand_should_not_substitute_pcre", function(cm, vim, helpers) { - cm.setCursor(0, 0); - CodeMirror.Vim.setOption('pcre', true); - helpers.doEx('%s/foo/namespace.\\&/'); - eq("namespace.&", cm.getValue()); - }, { value: 'foo' }); -testSubstitute('ex_substitute_backslashslash_regex', { - value: 'one\\two \n three\\four', - expectedValue: 'one,two \n three,four', - expr: '%s/\\\\/,'}); -testSubstitute('ex_substitute_slash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one/two \n three/four', - expr: '%s/,/\\/'}); -testSubstitute('ex_substitute_backslash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one\\two \n three\\four', - expr: '%s/,/\\\\/g'}); -testSubstitute('ex_substitute_multibackslash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one\\\\\\\\two \n three\\\\\\\\four', // 2*8 backslashes. - expr: '%s/,/\\\\\\\\\\\\\\\\/g'}); // 16 backslashes. -testSubstitute('ex_substitute_dollar_match', { - value: 'one,two \n three,four', - expectedValue: 'one,two ,\n three,four', - expr: '%s/$/,/g'}); -testSubstitute('ex_substitute_newline_match', { - value: 'one,two \n three,four', - expectedValue: 'one,two , three,four', - expr: '%s/\\n/,/g'}); -testSubstitute('ex_substitute_newline_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one\ntwo \n three\nfour', - expr: '%s/,/\\n/g'}); -testSubstitute('ex_substitute_braces_word', { - value: 'ababab abb ab{2}', - expectedValue: 'ab abb ab{2}', - expr: '%s/(ab){2}//g', - noPcreExpr: '%s/\\(ab\\)\\{2\\}//g'}); -testSubstitute('ex_substitute_braces_range', { - value: 'a aa aaa aaaa', - expectedValue: 'a a', - expr: '%s/a{2,3}//g', - noPcreExpr: '%s/a\\{2,3\\}//g'}); -testSubstitute('ex_substitute_braces_literal', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab abb ', - expr: '%s/ab\\{2\\}//g', - noPcreExpr: '%s/ab{2}//g'}); -testSubstitute('ex_substitute_braces_char', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab ab{2}', - expr: '%s/ab{2}//g', - noPcreExpr: '%s/ab\\{2\\}//g'}); -testSubstitute('ex_substitute_braces_no_escape', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab ab{2}', - expr: '%s/ab{2}//g', - noPcreExpr: '%s/ab\\{2}//g'}); -testSubstitute('ex_substitute_count', { - value: '1\n2\n3\n4', - expectedValue: '1\n0\n0\n4', - expr: 's/\\d/0/i 2'}); -testSubstitute('ex_substitute_count_with_range', { - value: '1\n2\n3\n4', - expectedValue: '1\n2\n0\n0', - expr: '1,3s/\\d/0/ 3'}); -testSubstitute('ex_substitute_not_global', { - value: 'aaa\nbaa\ncaa', - expectedValue: 'xaa\nbxa\ncxa', - expr: '%s/a/x/'}); -function testSubstituteConfirm(name, command, initialValue, expectedValue, keys, finalPos) { - testVim(name, function(cm, vim, helpers) { - var savedOpenDialog = cm.openDialog; - var savedKeyName = CodeMirror.keyName; - var onKeyDown; - var recordedCallback; - var closed = true; // Start out closed, set false on second openDialog. - function close() { - closed = true; - } - // First openDialog should save callback. - cm.openDialog = function(template, callback, options) { - recordedCallback = callback; - } - // Do first openDialog. - helpers.doKeys(':'); - // Second openDialog should save keyDown handler. - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - closed = false; - }; - // Return the command to Vim and trigger second openDialog. - recordedCallback(command); - // The event should really use keyCode, but here just mock it out and use - // key and replace keyName to just return key. - CodeMirror.keyName = function (e) { return e.key; } - keys = keys.toUpperCase(); - for (var i = 0; i < keys.length; i++) { - is(!closed); - onKeyDown({ key: keys.charAt(i) }, '', close); - } - try { - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(finalPos); - is(closed); - } catch(e) { - throw e - } finally { - // Restore overridden functions. - CodeMirror.keyName = savedKeyName; - cm.openDialog = savedOpenDialog; - } - }, { value: initialValue }); -} -testSubstituteConfirm('ex_substitute_confirm_emptydoc', - '%s/x/b/c', '', '', '', makeCursor(0, 0)); -testSubstituteConfirm('ex_substitute_confirm_nomatch', - '%s/x/b/c', 'ba a\nbab', 'ba a\nbab', '', makeCursor(0, 0)); -testSubstituteConfirm('ex_substitute_confirm_accept', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'yyy', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_random_keys', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ysdkywerty', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_some', - '%s/a/b/cg', 'ba a\nbab', 'bb a\nbbb', 'yny', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_all', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'a', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_accept_then_all', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ya', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_quit', - '%s/a/b/cg', 'ba a\nbab', 'bb a\nbab', 'yq', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_last', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_oneline', - '1s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_range_accept', - '1,2s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyy', makeCursor(1, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_some', - '1,3s/a/b/cg', 'aa\na \na\na', 'ba\nb \nb\na', 'ynyy', makeCursor(2, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_all', - '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \nb\na', 'a', makeCursor(2, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_last', - '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyl', makeCursor(1, 0)); -//:noh should clear highlighting of search-results but allow to resume search through n -testVim('ex_noh_clearSearchHighlight', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?'); - helpers.doEx('noh'); - eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); -}, { value: 'match nope match \n nope Match' }); -testVim('ex_yank', function (cm, vim, helpers) { - var curStart = makeCursor(3, 0); - cm.setCursor(curStart); - helpers.doEx('y'); - var register = helpers.getRegisterController().getRegister(); - var line = cm.getLine(3); - eq(line + '\n', register.toString()); -}); -testVim('set_boolean', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', true, 'boolean'); - // Test default value is set. - is(CodeMirror.Vim.getOption('testoption')); - // Test fail to set to non-boolean - var result = CodeMirror.Vim.setOption('testoption', '5'); - is(result instanceof Error); - // Test setOption - CodeMirror.Vim.setOption('testoption', false); - is(!CodeMirror.Vim.getOption('testoption')); -}); -testVim('ex_set_boolean', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', true, 'boolean'); - // Test default value is set. - is(CodeMirror.Vim.getOption('testoption')); - is(!cm.state.currentNotificationClose); - // Test fail to set to non-boolean - helpers.doEx('set testoption=22'); - is(cm.state.currentNotificationClose); - // Test setOption - helpers.doEx('set notestoption'); - is(!CodeMirror.Vim.getOption('testoption')); -}); -testVim('set_string', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', 'a', 'string'); - // Test default value is set. - eq('a', CodeMirror.Vim.getOption('testoption')); - // Test no fail to set non-string. - var result = CodeMirror.Vim.setOption('testoption', true); - is(!result); - // Test fail to set 'notestoption' - result = CodeMirror.Vim.setOption('notestoption', 'b'); - is(result instanceof Error); - // Test setOption - CodeMirror.Vim.setOption('testoption', 'c'); - eq('c', CodeMirror.Vim.getOption('testoption')); -}); -testVim('ex_set_string', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testopt', 'a', 'string'); - // Test default value is set. - eq('a', CodeMirror.Vim.getOption('testopt')); - // Test fail to set 'notestopt' - is(!cm.state.currentNotificationClose); - helpers.doEx('set notestopt=b'); - is(cm.state.currentNotificationClose); - // Test setOption - helpers.doEx('set testopt=c') - eq('c', CodeMirror.Vim.getOption('testopt')); - helpers.doEx('set testopt=c') - eq('c', CodeMirror.Vim.getOption('testopt', cm)); //local || global - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); // local - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); // global - eq('c', CodeMirror.Vim.getOption('testopt')); // global - // Test setOption global - helpers.doEx('setg testopt=d') - eq('c', CodeMirror.Vim.getOption('testopt', cm)); - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); - eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); - eq('d', CodeMirror.Vim.getOption('testopt')); - // Test setOption local - helpers.doEx('setl testopt=e') - eq('e', CodeMirror.Vim.getOption('testopt', cm)); - eq('e', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); - eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); - eq('d', CodeMirror.Vim.getOption('testopt')); -}); -testVim('ex_set_callback', function(cm, vim, helpers) { - var global; - - function cb(val, cm, cfg) { - if (val === undefined) { - // Getter - if (cm) { - return cm._local; - } else { - return global; - } - } else { - // Setter - if (cm) { - cm._local = val; - } else { - global = val; - } - } - } - - CodeMirror.Vim.defineOption('testopt', 'a', 'string', cb); - // Test default value is set. - eq('a', CodeMirror.Vim.getOption('testopt')); - // Test fail to set 'notestopt' - is(!cm.state.currentNotificationClose); - helpers.doEx('set notestopt=b'); - is(cm.state.currentNotificationClose); - // Test setOption (Identical to the string tests, but via callback instead) - helpers.doEx('set testopt=c') - eq('c', CodeMirror.Vim.getOption('testopt', cm)); //local || global - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); // local - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); // global - eq('c', CodeMirror.Vim.getOption('testopt')); // global - // Test setOption global - helpers.doEx('setg testopt=d') - eq('c', CodeMirror.Vim.getOption('testopt', cm)); - eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); - eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); - eq('d', CodeMirror.Vim.getOption('testopt')); - // Test setOption local - helpers.doEx('setl testopt=e') - eq('e', CodeMirror.Vim.getOption('testopt', cm)); - eq('e', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); - eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); - eq('d', CodeMirror.Vim.getOption('testopt')); -}) -testVim('ex_set_filetype', function(cm, vim, helpers) { - CodeMirror.defineMode('test_mode', function() { - return {token: function(stream) { - stream.match(/^\s+|^\S+/); - }}; - }); - CodeMirror.defineMode('test_mode_2', function() { - return {token: function(stream) { - stream.match(/^\s+|^\S+/); - }}; - }); - // Test mode is set. - helpers.doEx('set filetype=test_mode'); - eq('test_mode', cm.getMode().name); - // Test 'ft' alias also sets mode. - helpers.doEx('set ft=test_mode_2'); - eq('test_mode_2', cm.getMode().name); -}); -testVim('ex_set_filetype_null', function(cm, vim, helpers) { - CodeMirror.defineMode('test_mode', function() { - return {token: function(stream) { - stream.match(/^\s+|^\S+/); - }}; - }); - cm.setOption('mode', 'test_mode'); - // Test mode is set to null. - helpers.doEx('set filetype='); - eq('null', cm.getMode().name); -}); - -testVim('mapclear', function(cm, vim, helpers) { - CodeMirror.Vim.map('w', 'l'); - cm.setCursor(0, 0); - helpers.assertCursorAt(0, 0); - helpers.doKeys('w'); - helpers.assertCursorAt(0, 1); - CodeMirror.Vim.mapclear('visual'); - helpers.doKeys('v', 'w', 'v'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('w'); - helpers.assertCursorAt(0, 5); - CodeMirror.Vim.mapclear(); -}, { value: 'abc abc' }); -testVim('mapclear_context', function(cm, vim, helpers) { - CodeMirror.Vim.map('w', 'l', 'normal'); - cm.setCursor(0, 0); - helpers.assertCursorAt(0, 0); - helpers.doKeys('w'); - helpers.assertCursorAt(0, 1); - CodeMirror.Vim.mapclear('normal'); - helpers.doKeys('w'); - helpers.assertCursorAt(0, 4); - CodeMirror.Vim.mapclear(); -}, { value: 'abc abc' }); - -testVim('ex_map_key2key', function(cm, vim, helpers) { - helpers.doEx('map a x'); - helpers.doKeys('a'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); - CodeMirror.Vim.mapclear(); -}, { value: 'abc' }); -testVim('ex_unmap_key2key', function(cm, vim, helpers) { - helpers.doEx('map a x'); - helpers.doEx('unmap a'); - helpers.doKeys('a'); - eq('vim-insert', cm.getOption('keyMap')); - CodeMirror.Vim.mapclear(); -}, { value: 'abc' }); -testVim('ex_unmap_key2key_does_not_remove_default', function(cm, vim, helpers) { - expectFail(function() { - helpers.doEx('unmap a'); - }); - helpers.doKeys('a'); - eq('vim-insert', cm.getOption('keyMap')); - CodeMirror.Vim.mapclear(); -}, { value: 'abc' }); -testVim('ex_map_key2key_to_colon', function(cm, vim, helpers) { - helpers.doEx('map ; :'); - var dialogOpened = false; - cm.openDialog = function() { - dialogOpened = true; - } - helpers.doKeys(';'); - eq(dialogOpened, true); - CodeMirror.Vim.mapclear(); -}); -testVim('ex_map_ex2key:', function(cm, vim, helpers) { - helpers.doEx('map :del x'); - helpers.doEx('del'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); - CodeMirror.Vim.mapclear(); -}, { value: 'abc' }); -testVim('ex_map_ex2ex', function(cm, vim, helpers) { - helpers.doEx('map :del :w'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - helpers.doEx('del'); - CodeMirror.commands.save = tmp; - eq(written, true); - eq(actualCm, cm); - CodeMirror.Vim.mapclear(); -}); -testVim('ex_map_key2ex', function(cm, vim, helpers) { - helpers.doEx('map a :w'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - helpers.doKeys('a'); - CodeMirror.commands.save = tmp; - eq(written, true); - eq(actualCm, cm); - CodeMirror.Vim.mapclear(); -}); -testVim('ex_map_key2key_visual_api', function(cm, vim, helpers) { - CodeMirror.Vim.map('b', ':w', 'visual'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - // Mapping should not work in normal mode. - helpers.doKeys('b'); - eq(written, false); - // Mapping should work in visual mode. - helpers.doKeys('v', 'b'); - eq(written, true); - eq(actualCm, cm); - - CodeMirror.commands.save = tmp; - CodeMirror.Vim.mapclear(); -}); -testVim('ex_imap', function(cm, vim, helpers) { - CodeMirror.Vim.map('jk', '', 'insert'); - helpers.doKeys('i'); - is(vim.insertMode); - helpers.doKeys('j', 'k'); - is(!vim.insertMode); - cm.setCursor(0, 1); - CodeMirror.Vim.map('jj', '', 'insert'); - helpers.doKeys('', '2', 'j', 'l', 'c'); - helpers.doKeys('f', 'o'); - eq('1fo4\n5fo8\nafodefg', cm.getValue()); - helpers.doKeys('j', 'j'); - cm.setCursor(0, 0); - helpers.doKeys('.'); - eq('foo4\nfoo8\nfoodefg', cm.getValue()); - CodeMirror.Vim.mapclear(); -}, { value: '1234\n5678\nabcdefg' }); -testVim('ex_unmap_api', function(cm, vim, helpers) { - CodeMirror.Vim.map('', 'gg', 'normal'); - is(CodeMirror.Vim.handleKey(cm, "", "normal"), "Alt-X key is mapped"); - CodeMirror.Vim.unmap("", "normal"); - is(!CodeMirror.Vim.handleKey(cm, "", "normal"), "Alt-X key is unmapped"); - CodeMirror.Vim.mapclear(); -}); -// Testing registration of functions as ex-commands and mapping to -keys -testVim('ex_api_test', function(cm, vim, helpers) { - var res=false; - var val='from'; - CodeMirror.Vim.defineEx('extest','ext',function(cm,params){ - if(params.args)val=params.args[0]; - else res=true; - }); - helpers.doEx(':ext to'); - eq(val,'to','Defining ex-command failed'); - CodeMirror.Vim.map('',':ext'); - helpers.doKeys('',''); - is(res,'Mapping to key failed'); - CodeMirror.Vim.mapclear(); -}); -// Testing ex-commands with non-alpha names. -testVim('ex_special_names', function(cm, vim, helpers) { - var ran,val; - var cmds = ['!','!!','#','&','*','<','=','>','@','@@','~','regtest1','RT2']; - cmds.forEach(function(name){ - CodeMirror.Vim.defineEx(name,'',function(cm,params){ - ran=params.commandName; - val=params.argString; - }); - helpers.doEx(':'+name); - eq(ran,name,'Running ex-command failed'); - helpers.doEx(':'+name+' x'); - eq(val,' x','Running ex-command with param failed: '+name); - if(/^\W+$/.test(name)){ - helpers.doEx(':'+name+'y'); - eq(val,'y','Running ex-command with param failed: '+name); - } - else{ - helpers.doEx(':'+name+'-y'); - eq(val,'-y','Running ex-command with param failed: '+name); - } - if(name!=='!'){ - helpers.doEx(':'+name+'!'); - eq(ran,name,'Running ex-command with bang failed'); - eq(val,'!','Running ex-command with bang failed: '+name); - helpers.doEx(':'+name+'!z'); - eq(ran,name,'Running ex-command with bang & param failed'); - eq(val,'!z','Running ex-command with bang & param failed: '+name); - } - }); -}); -// For now, this test needs to be last because it messes up : for future tests. -testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) { - helpers.doEx('map : x'); - helpers.doKeys(':'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); - CodeMirror.Vim.mapclear(); -}, { value: 'abc' }); - -testVim('noremap', function(cm, vim, helpers) { - CodeMirror.Vim.noremap(';', 'l'); - cm.setCursor(0, 0); - eq('wOrd1', cm.getValue()); - // Mapping should work in normal mode. - helpers.doKeys(';', 'r', '1'); - eq('w1rd1', cm.getValue()); - // Mapping will not work in insert mode because of no current fallback - // keyToKey mapping support. - helpers.doKeys('i', ';', ''); - eq('w;1rd1', cm.getValue()); - // unmap all mappings - CodeMirror.Vim.mapclear(); -}, { value: 'wOrd1' }); -testVim('noremap_swap', function(cm, vim, helpers) { - CodeMirror.Vim.noremap('i', 'a', 'normal'); - CodeMirror.Vim.noremap('a', 'i', 'normal'); - cm.setCursor(0, 0); - // 'a' should act like 'i'. - helpers.doKeys('a'); - eqCursorPos(Pos(0, 0), cm.getCursor()); - // ...and 'i' should act like 'a'. - helpers.doKeys('', 'i'); - eqCursorPos(Pos(0, 1), cm.getCursor()); - // unmap all mappings - CodeMirror.Vim.mapclear(); -}, { value: 'foo' }); -testVim('noremap_map_interaction', function(cm, vim, helpers) { - // noremap should clobber map - CodeMirror.Vim.map(';', 'l'); - CodeMirror.Vim.noremap(';', 'l'); - CodeMirror.Vim.map('l', 'j'); - cm.setCursor(0, 0); - helpers.doKeys(';'); - eqCursorPos(Pos(0, 1), cm.getCursor()); - helpers.doKeys('l'); - eqCursorPos(Pos(1, 1), cm.getCursor()); - // map should be able to point to a noremap - CodeMirror.Vim.map('m', ';'); - helpers.doKeys('m'); - eqCursorPos(Pos(1, 2), cm.getCursor()); - // unmap all mappings - CodeMirror.Vim.mapclear(); -}, { value: 'wOrd1\nwOrd2' }); -testVim('noremap_map_interaction2', function(cm, vim, helpers) { - // map should point to the most recent noremap - CodeMirror.Vim.noremap(';', 'l'); - CodeMirror.Vim.map('m', ';'); - CodeMirror.Vim.noremap(';', 'h'); - cm.setCursor(0, 0); - helpers.doKeys('l'); - eqCursorPos(Pos(0, 1), cm.getCursor()); - helpers.doKeys('m'); - eqCursorPos(Pos(0, 0), cm.getCursor()); - // unmap all mappings - CodeMirror.Vim.mapclear(); -}, { value: 'wOrd1\nwOrd2' }); - -// Test event handlers -testVim('beforeSelectionChange', function(cm, vim, helpers) { - cm.setCursor(0, 100); - eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor')); -}, { value: 'abc' }); - -testVim('increment_binary', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys(''); - eq('0b001', cm.getValue()); - helpers.doKeys(''); - eq('0b010', cm.getValue()); - helpers.doKeys(''); - eq('0b001', cm.getValue()); - helpers.doKeys(''); - eq('0b000', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('0b001', cm.getValue()); - helpers.doKeys(''); - eq('0b010', cm.getValue()); - helpers.doKeys(''); - eq('0b001', cm.getValue()); - helpers.doKeys(''); - eq('0b000', cm.getValue()); -}, { value: '0b000' }); - -testVim('increment_octal', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys(''); - eq('001', cm.getValue()); - helpers.doKeys(''); - eq('002', cm.getValue()); - helpers.doKeys(''); - eq('003', cm.getValue()); - helpers.doKeys(''); - eq('004', cm.getValue()); - helpers.doKeys(''); - eq('005', cm.getValue()); - helpers.doKeys(''); - eq('006', cm.getValue()); - helpers.doKeys(''); - eq('007', cm.getValue()); - helpers.doKeys(''); - eq('010', cm.getValue()); - helpers.doKeys(''); - eq('007', cm.getValue()); - helpers.doKeys(''); - eq('006', cm.getValue()); - helpers.doKeys(''); - eq('005', cm.getValue()); - helpers.doKeys(''); - eq('004', cm.getValue()); - helpers.doKeys(''); - eq('003', cm.getValue()); - helpers.doKeys(''); - eq('002', cm.getValue()); - helpers.doKeys(''); - eq('001', cm.getValue()); - helpers.doKeys(''); - eq('000', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('001', cm.getValue()); - helpers.doKeys(''); - eq('002', cm.getValue()); - helpers.doKeys(''); - eq('001', cm.getValue()); - helpers.doKeys(''); - eq('000', cm.getValue()); -}, { value: '000' }); - -testVim('increment_decimal', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys(''); - eq('101', cm.getValue()); - helpers.doKeys(''); - eq('102', cm.getValue()); - helpers.doKeys(''); - eq('103', cm.getValue()); - helpers.doKeys(''); - eq('104', cm.getValue()); - helpers.doKeys(''); - eq('105', cm.getValue()); - helpers.doKeys(''); - eq('106', cm.getValue()); - helpers.doKeys(''); - eq('107', cm.getValue()); - helpers.doKeys(''); - eq('108', cm.getValue()); - helpers.doKeys(''); - eq('109', cm.getValue()); - helpers.doKeys(''); - eq('110', cm.getValue()); - helpers.doKeys(''); - eq('109', cm.getValue()); - helpers.doKeys(''); - eq('108', cm.getValue()); - helpers.doKeys(''); - eq('107', cm.getValue()); - helpers.doKeys(''); - eq('106', cm.getValue()); - helpers.doKeys(''); - eq('105', cm.getValue()); - helpers.doKeys(''); - eq('104', cm.getValue()); - helpers.doKeys(''); - eq('103', cm.getValue()); - helpers.doKeys(''); - eq('102', cm.getValue()); - helpers.doKeys(''); - eq('101', cm.getValue()); - helpers.doKeys(''); - eq('100', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('101', cm.getValue()); - helpers.doKeys(''); - eq('102', cm.getValue()); - helpers.doKeys(''); - eq('101', cm.getValue()); - helpers.doKeys(''); - eq('100', cm.getValue()); -}, { value: '100' }); - -testVim('increment_decimal_single_zero', function(cm, vim, helpers) { - helpers.doKeys(''); - eq('1', cm.getValue()); - helpers.doKeys(''); - eq('2', cm.getValue()); - helpers.doKeys(''); - eq('3', cm.getValue()); - helpers.doKeys(''); - eq('4', cm.getValue()); - helpers.doKeys(''); - eq('5', cm.getValue()); - helpers.doKeys(''); - eq('6', cm.getValue()); - helpers.doKeys(''); - eq('7', cm.getValue()); - helpers.doKeys(''); - eq('8', cm.getValue()); - helpers.doKeys(''); - eq('9', cm.getValue()); - helpers.doKeys(''); - eq('10', cm.getValue()); - helpers.doKeys(''); - eq('9', cm.getValue()); - helpers.doKeys(''); - eq('8', cm.getValue()); - helpers.doKeys(''); - eq('7', cm.getValue()); - helpers.doKeys(''); - eq('6', cm.getValue()); - helpers.doKeys(''); - eq('5', cm.getValue()); - helpers.doKeys(''); - eq('4', cm.getValue()); - helpers.doKeys(''); - eq('3', cm.getValue()); - helpers.doKeys(''); - eq('2', cm.getValue()); - helpers.doKeys(''); - eq('1', cm.getValue()); - helpers.doKeys(''); - eq('0', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('1', cm.getValue()); - helpers.doKeys(''); - eq('2', cm.getValue()); - helpers.doKeys(''); - eq('1', cm.getValue()); - helpers.doKeys(''); - eq('0', cm.getValue()); -}, { value: '0' }); - -testVim('increment_hexadecimal', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys(''); - eq('0x1', cm.getValue()); - helpers.doKeys(''); - eq('0x2', cm.getValue()); - helpers.doKeys(''); - eq('0x3', cm.getValue()); - helpers.doKeys(''); - eq('0x4', cm.getValue()); - helpers.doKeys(''); - eq('0x5', cm.getValue()); - helpers.doKeys(''); - eq('0x6', cm.getValue()); - helpers.doKeys(''); - eq('0x7', cm.getValue()); - helpers.doKeys(''); - eq('0x8', cm.getValue()); - helpers.doKeys(''); - eq('0x9', cm.getValue()); - helpers.doKeys(''); - eq('0xa', cm.getValue()); - helpers.doKeys(''); - eq('0xb', cm.getValue()); - helpers.doKeys(''); - eq('0xc', cm.getValue()); - helpers.doKeys(''); - eq('0xd', cm.getValue()); - helpers.doKeys(''); - eq('0xe', cm.getValue()); - helpers.doKeys(''); - eq('0xf', cm.getValue()); - helpers.doKeys(''); - eq('0x10', cm.getValue()); - helpers.doKeys(''); - eq('0x0f', cm.getValue()); - helpers.doKeys(''); - eq('0x0e', cm.getValue()); - helpers.doKeys(''); - eq('0x0d', cm.getValue()); - helpers.doKeys(''); - eq('0x0c', cm.getValue()); - helpers.doKeys(''); - eq('0x0b', cm.getValue()); - helpers.doKeys(''); - eq('0x0a', cm.getValue()); - helpers.doKeys(''); - eq('0x09', cm.getValue()); - helpers.doKeys(''); - eq('0x08', cm.getValue()); - helpers.doKeys(''); - eq('0x07', cm.getValue()); - helpers.doKeys(''); - eq('0x06', cm.getValue()); - helpers.doKeys(''); - eq('0x05', cm.getValue()); - helpers.doKeys(''); - eq('0x04', cm.getValue()); - helpers.doKeys(''); - eq('0x03', cm.getValue()); - helpers.doKeys(''); - eq('0x02', cm.getValue()); - helpers.doKeys(''); - eq('0x01', cm.getValue()); - helpers.doKeys(''); - eq('0x00', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('0x01', cm.getValue()); - helpers.doKeys(''); - eq('0x02', cm.getValue()); - helpers.doKeys(''); - eq('0x01', cm.getValue()); - helpers.doKeys(''); - eq('0x00', cm.getValue()); -}, { value: '0x0' });