├── .editorconfig ├── .gitattributes ├── .github ├── CODEOWNERS └── workflows │ └── build-docs.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── env.d.ts ├── eslint.config.ts ├── index.html ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── public └── .gitignore ├── src-docs ├── App.vue ├── DemoPage.vue ├── components │ ├── LinterAndCrossBindingDemo.vue │ ├── MarkdownDemo.vue │ ├── ReadonlyAndDisabledDemo.vue │ ├── SlotDemo.vue │ └── ToggleTheme.vue ├── main.ts └── style.scss ├── src ├── components │ └── CodeMirror.ts ├── helpers │ └── h-demi.ts ├── index.ts └── interfaces │ └── MetaInterface.ts ├── tsconfig.app.json ├── tsconfig.docs.json ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | * vue eol=lf encoding=UTF-8 4 | *.css eol=lf encoding=UTF-8 5 | *.html eol=lf encoding=UTF-8 6 | *.js eol=lf encoding=UTF-8 7 | *.jsx eol=lf encoding=UTF-8 8 | *.md eol=lf encoding=UTF-8 9 | *.scss eol=lf encoding=UTF-8 10 | *.ts eol=lf encoding=UTF-8 11 | *.tsx eol=lf encoding=UTF-8 12 | *.txt eol=lf encoding=UTF-8 13 | *.xml eol=lf encoding=UTF-8 14 | 15 | # Images 16 | *.gif binary 17 | *.ico binary 18 | *.jpg binary 19 | *.png binary 20 | *.svg eol=lf encoding=UTF-8 21 | *.webp binary 22 | 23 | # Fonts 24 | *.eot binary 25 | *.otf binary 26 | *.ttf binary 27 | *.woff binary 28 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Generated by CODEOWNERS.com 2 | 3 | -------------------------------------------------------------------------------- /.github/workflows/build-docs.yml: -------------------------------------------------------------------------------- 1 | name: NodeJS with Vite 2 | 3 | on: 4 | push: 5 | branches: ['master'] 6 | pull_request: 7 | branches: ['master'] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [22.x] 16 | 17 | steps: 18 | - name: Checkout ✅ 19 | uses: actions/checkout@v4.2.2 20 | 21 | - name: Use Node.js ${{ matrix.node-version }} ⚡ 22 | uses: actions/setup-node@v4.1.0 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | 26 | - name: Install pnpm 🎁 27 | uses: pnpm/action-setup@v4 28 | 29 | - name: Build 🔧 30 | run: | 31 | pnpm install 32 | pnpm run build:docs 33 | 34 | - name: Deploy to gh-pages 🚀 35 | uses: JamesIves/github-pages-deploy-action@v4.6.8 36 | with: 37 | branch: gh-pages # The branch the action should deploy to. 38 | folder: docs # The folder the action should deploy. 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.yarn/* 2 | # !/.yarn/patches 3 | # !/.yarn/plugins 4 | !/.yarn/releases 5 | # !/.yarn/sdks 6 | # !/.yarn/cache 7 | /.vscode/* 8 | node_modules 9 | .DS_Store 10 | dist 11 | dist-ssr 12 | *.local 13 | /src/Meta.ts 14 | docs/ 15 | stats.html 16 | *.tsbuildinfo 17 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.11.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .husky/ 2 | .vscode/ 3 | .yarn/ 4 | dist/ 5 | public/assets/ 6 | docs/ 7 | stats.html 8 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | printWidth: 80 2 | tabWidth: 2 3 | useTabs: false 4 | semi: true 5 | singleQuote: true 6 | trailingComma: es5 7 | bracketSpacing: true 8 | bracketSameLine: false 9 | arrowParens: avoid 10 | htmlWhitespaceSensitivity: ignore 11 | endOfLine: lf 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | 3 | All notable changes to this project will be documented in this file. Dates are displayed in UTC. 4 | 5 | Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). 6 | 7 | #### [1.3.17](https://github.com/logue/vue-codemirror6/compare/1.3.13...1.3.17) 8 | 9 | - fix: full example link [`#58`](https://github.com/logue/vue-codemirror6/pull/58) 10 | - Update dependencies. [`985f36f`](https://github.com/logue/vue-codemirror6/commit/985f36f23c4fc1b562b53b664deb7ff1eec79989) 11 | - Removed unnecessary comment. [`8d624a4`](https://github.com/logue/vue-codemirror6/commit/8d624a46cebc0f9196e5987f793e90184332ae4b) 12 | - Fixed typo. [`b39f9cb`](https://github.com/logue/vue-codemirror6/commit/b39f9cb0993708338f0a96429b4cdc2fd510208e) 13 | 14 | #### [1.3.13](https://github.com/logue/vue-codemirror6/compare/1.3.8...1.3.13) 15 | 16 | > 31 March 2025 17 | 18 | - Rewrite to eslint.config.ts [`841f686`](https://github.com/logue/vue-codemirror6/commit/841f686ebab5f542a5b7bc1569660ce58a21af65) 19 | - Fixed types path. [`2b86b2c`](https://github.com/logue/vue-codemirror6/commit/2b86b2c4da906f4591e13221e5c41e52b8792523) 20 | - Update @codemirror/language, @codemirror/lint, @codemirror/state, @codemirror/view. [`916fa98`](https://github.com/logue/vue-codemirror6/commit/916fa9899e7c3806199f53bddb210d3cba9e7517) 21 | 22 | #### [1.3.8](https://github.com/logue/vue-codemirror6/compare/1.1.16...1.3.8) 23 | 24 | > 22 November 2024 25 | 26 | - Fix sample code syntax error [`#47`](https://github.com/logue/vue-codemirror6/pull/47) 27 | - chore: demo docs [`#43`](https://github.com/logue/vue-codemirror6/pull/43) 28 | - fix: editor not update content when `modelValue` change and selection is out of range [`#44`](https://github.com/logue/vue-codemirror6/pull/44) 29 | - chore: props and readme doc [`#31`](https://github.com/logue/vue-codemirror6/pull/31) 30 | - Reduce unnecessary updates [`#22`](https://github.com/logue/vue-codemirror6/pull/22) 31 | - fix: coalescing-operator [`#20`](https://github.com/logue/vue-codemirror6/pull/20) 32 | - Migrate to pnpm. [`6d83aec`](https://github.com/logue/vue-codemirror6/commit/6d83aec625d50228cbfb248bc5811b6f83975219) 33 | - Update dependencies. [`992f209`](https://github.com/logue/vue-codemirror6/commit/992f209c5e4c269b72be119f693b0d0376d529bb) 34 | - Update dependencies. [`b839a33`](https://github.com/logue/vue-codemirror6/commit/b839a33569fadf3a9bb5e2a8f972c377fc1fce88) 35 | 36 | #### [1.1.16](https://github.com/logue/vue-codemirror6/compare/1.1.13...1.1.16) 37 | 38 | > 18 March 2023 39 | 40 | - Expose `view`. #16 [`daf3e6b`](https://github.com/logue/vue-codemirror6/commit/daf3e6b17a161a5c0c5a35ea2442d85421f05d82) 41 | - Fixed Maximum call stack size exceeded error. [`f10eaae`](https://github.com/logue/vue-codemirror6/commit/f10eaaea9145bc1d7a6605ab3cb8a82c0ca12df6) 42 | - Partially fixed the problem that the values defined in props disappeared under certain conditions. [`1cc4468`](https://github.com/logue/vue-codemirror6/commit/1cc4468e35e39ee8eaae1b2f007e3425a757217f) 43 | 44 | #### [1.1.13](https://github.com/logue/vue-codemirror6/compare/1.1.12...1.1.13) 45 | 46 | > 13 March 2023 47 | 48 | - Changed minimum Vue version requirement to 2.7.14. [`5b8b255`](https://github.com/logue/vue-codemirror6/commit/5b8b2558967f9fb6f6d9e83a0181afa50f182b98) 49 | - Add allow-multiple-selections prop. [`ced91ed`](https://github.com/logue/vue-codemirror6/commit/ced91edab8fc25f8f1299341489f6bbbdace9ce8) 50 | - Update CHANGELOG.md [`51d30e2`](https://github.com/logue/vue-codemirror6/commit/51d30e2f6370847ef888ab2b39ff6e1c8e3ec851) 51 | 52 | #### [1.1.12](https://github.com/logue/vue-codemirror6/compare/1.1.11...1.1.12) 53 | 54 | > 10 March 2023 55 | 56 | - chore(CODEOWNERS): CODEOWNERS.com Bot, at your service! [`#14`](https://github.com/logue/vue-codemirror6/pull/14) 57 | - Update demo code. (Replaced @codemirror/html to @codemirror/vue.) [`8da1a8a`](https://github.com/logue/vue-codemirror6/commit/8da1a8a4e37d713cc78281ce9c304c42a06940dc) 58 | - Changed implementation to generate *.d.ts with vite-plugin-dts. [`1caaa20`](https://github.com/logue/vue-codemirror6/commit/1caaa20b1423a3d252a6c770a630fb60e2e3440d) 59 | - Update README.md. [`f8dd493`](https://github.com/logue/vue-codemirror6/commit/f8dd4934a7b9cc089cd830473db5250d28b3389b) 60 | 61 | #### [1.1.11](https://github.com/logue/vue-codemirror6/compare/1.1.3...1.1.11) 62 | 63 | > 4 February 2023 64 | 65 | - [SECURITY FIX] CWE-1333: Inefficient Regular Expression Complexity #13 [`fc63a9f`](https://github.com/logue/vue-codemirror6/commit/fc63a9f14b5307089e9551157b011072971b112f) 66 | - Fix for #11 [`c634b55`](https://github.com/logue/vue-codemirror6/commit/c634b555ffff93bb30da2f501ec366289fc86ec1) 67 | - Add forceReconfigure() function. [`bbfa8ad`](https://github.com/logue/vue-codemirror6/commit/bbfa8adb5bd5cdb56a53bd69337a4e0313e52033) 68 | 69 | #### [1.1.3](https://github.com/logue/vue-codemirror6/compare/1.1.2...1.1.3) 70 | 71 | > 9 January 2023 72 | 73 | - Bump json5 from 1.0.1 to 1.0.2 [`#10`](https://github.com/logue/vue-codemirror6/pull/10) 74 | - Consolidated demo code generation settings into a single vite.config.ts [`7fe3ad2`](https://github.com/logue/vue-codemirror6/commit/7fe3ad25676eb34844b7bfe64b5dc73f30211a5b) 75 | - Update dependencies. [`75b984e`](https://github.com/logue/vue-codemirror6/commit/75b984e38c13114ce30b853fba64493c55368be3) 76 | 77 | #### [1.1.2](https://github.com/logue/vue-codemirror6/compare/1.1.1...1.1.2) 78 | 79 | > 24 December 2022 80 | 81 | - Update CHANGELOG.md. [`72efba5`](https://github.com/logue/vue-codemirror6/commit/72efba5e9e272da2dcb3bd941079ee415d1b4c53) 82 | - Minor fix. [`d1e3118`](https://github.com/logue/vue-codemirror6/commit/d1e3118ca2f2efe4d63f64e505d8a01a7c29a104) 83 | - Update dependencies. [`d747a21`](https://github.com/logue/vue-codemirror6/commit/d747a21778b42ed12921dd468a91a910895925e8) 84 | 85 | #### [1.1.1](https://github.com/logue/vue-codemirror6/compare/1.1.0...1.1.1) 86 | 87 | > 7 November 2022 88 | 89 | - Fixed Markdown demo. [`399f32c`](https://github.com/logue/vue-codemirror6/commit/399f32c95440b24c253110670d46811673cf38bd) 90 | - Fix cross-binding not works. [`137765b`](https://github.com/logue/vue-codemirror6/commit/137765b23c0b27ba3c4784d0708ee7172c94083a) 91 | - Fixed taking URL class from other than node when building docs. [`fbf1d56`](https://github.com/logue/vue-codemirror6/commit/fbf1d56296b4b739b7ff84107ec33eb3681a0398) 92 | 93 | #### [1.1.0](https://github.com/logue/vue-codemirror6/compare/1.0.3...1.1.0) 94 | 95 | > 31 October 2022 96 | 97 | - Rewrite the demo code with Vue3 setup. [`fdc7e3c`](https://github.com/logue/vue-codemirror6/commit/fdc7e3c172ff8f1710a74459b1ed7da70420b732) 98 | - Fixed wrong typing of computed value. [`9b5a572`](https://github.com/logue/vue-codemirror6/commit/9b5a572c5a18bfd3cf9cd5f4c0b0866efab9e893) 99 | - Add CHANGELOG.md. [`1de388f`](https://github.com/logue/vue-codemirror6/commit/1de388f5126a6afeaec250adefc82bc709251bbd) 100 | 101 | #### [1.0.3](https://github.com/logue/vue-codemirror6/compare/1.0.2...1.0.3) 102 | 103 | > 12 October 2022 104 | 105 | - Update dependencies. [`7f096da`](https://github.com/logue/vue-codemirror6/commit/7f096da24bfc50c9f2932cac6ad023d3982281bf) 106 | - Update demo code. [`ac33441`](https://github.com/logue/vue-codemirror6/commit/ac33441c2886664c11a91b5b838db74220f0b565) 107 | - Fixed vite.config.docs.ts. [`b13628a`](https://github.com/logue/vue-codemirror6/commit/b13628aeaa29df6c525b899776ae7706cdf800bc) 108 | 109 | #### [1.0.2](https://github.com/logue/vue-codemirror6/compare/1.0.1...1.0.2) 110 | 111 | > 15 September 2022 112 | 113 | - Enabled to specify HTML tags used in components. [`9b9eecd`](https://github.com/logue/vue-codemirror6/commit/9b9eecdb4da434295bf176c4c17d832871aba927) 114 | 115 | #### [1.0.1](https://github.com/logue/vue-codemirror6/compare/1.0.0...1.0.1) 116 | 117 | > 8 September 2022 118 | 119 | - Update dependencies. [`f3631d0`](https://github.com/logue/vue-codemirror6/commit/f3631d0c3c00a07ba8e69916116db1d611397fb9) 120 | - Disable drop console. [`e27b9ef`](https://github.com/logue/vue-codemirror6/commit/e27b9efea75180b3c6031c8e3a06c2bd5cee228b) 121 | 122 | ### [1.0.0](https://github.com/logue/vue-codemirror6/compare/0.6.8...1.0.0) 123 | 124 | > 20 August 2022 125 | 126 | - Fixed editable and readonly toggle. [`97ac1bf`](https://github.com/logue/vue-codemirror6/commit/97ac1bf8b24775daefb58af91c318771ea355c8d) 127 | - 1.0 [`4493c74`](https://github.com/logue/vue-codemirror6/commit/4493c744d38002fbca943f43b14985a3ae2c592c) 128 | 129 | #### [0.6.8](https://github.com/logue/vue-codemirror6/compare/0.6.5...0.6.8) 130 | 131 | > 1 August 2022 132 | 133 | - Unify props monitoring process. [`15b8a17`](https://github.com/logue/vue-codemirror6/commit/15b8a171cec652a03800fc9b53c9f3c5b78f74bf) 134 | - Fixed an error around linter. [`b2fe3db`](https://github.com/logue/vue-codemirror6/commit/b2fe3dba9f6edd3167edeeff08f34139e444ecd7) 135 | - Fixed dispatch may be executed multiple times when the value of prop is changed. [`2e19316`](https://github.com/logue/vue-codemirror6/commit/2e193168876e7e0da06695e68176433fd328c0b7) 136 | 137 | #### [0.6.5](https://github.com/logue/vue-codemirror6/compare/0.6.4...0.6.5) 138 | 139 | > 4 July 2022 140 | 141 | - Remove banner from source code. [`043e0ed`](https://github.com/logue/vue-codemirror6/commit/043e0ed0769c0b3bf52777e0d64199e713377418) 142 | - Delete unnecessary cursor movement processing and assignment processing. [`c89fd00`](https://github.com/logue/vue-codemirror6/commit/c89fd0020e241dca2d8292511a2b55ffbece5a82) 143 | 144 | #### [0.6.4](https://github.com/logue/vue-codemirror6/compare/0.6.0...0.6.4) 145 | 146 | > 28 June 2022 147 | 148 | - Fixed a bug that the cursor may move to a strange place when inputting. [`9977976`](https://github.com/logue/vue-codemirror6/commit/9977976678fda8a268a5c998f6bb95bdf0b86a1e) 149 | - Changed the logic when assigning text values to CodeMirror. [`313e701`](https://github.com/logue/vue-codemirror6/commit/313e70196573f9365519a7558cbc0f05c193a000) 150 | - Fixed the problem that the definition file was omitted because the binary output by analyze was given to npm. [`503a52e`](https://github.com/logue/vue-codemirror6/commit/503a52ef7c5077241b72909ee27afb262b52312e) 151 | 152 | #### [0.6.0](https://github.com/logue/vue-codemirror6/compare/0.5.5...0.6.0) 153 | 154 | > 9 June 2022 155 | 156 | - Added minimal prop. [`d454d25`](https://github.com/logue/vue-codemirror6/commit/d454d257c1fd04b765f5fdb555b240395fe030b5) 157 | - Migrate to CodeMirror 6.0.0. [`50cb696`](https://github.com/logue/vue-codemirror6/commit/50cb696dcc3af61d77a7e74ef37041d20bfb71fc) 158 | - Add methods jsdoc and manual. [`44975eb`](https://github.com/logue/vue-codemirror6/commit/44975ebedf76d1179ab2c1835e9f16b8e583a64a) 159 | 160 | #### [0.5.5](https://github.com/logue/vue-codemirror6/compare/0.5.4...0.5.5) 161 | 162 | > 8 June 2022 163 | 164 | - Urgent release. [`7db9007`](https://github.com/logue/vue-codemirror6/commit/7db9007886b5daeea94f7986d256291911b7a55c) 165 | 166 | #### [0.5.4](https://github.com/logue/vue-codemirror6/compare/0.5.3...0.5.4) 167 | 168 | > 31 May 2022 169 | 170 | - Fixed a bug that the cursor goes to a strange place when inputting. [`81af5b2`](https://github.com/logue/vue-codemirror6/commit/81af5b2e950cd972bfe540621fb75a6a8e18d544) 171 | 172 | #### [0.5.3](https://github.com/logue/vue-codemirror6/compare/0.5.2...0.5.3) 173 | 174 | > 31 May 2022 175 | 176 | - Fixed an issue where parent-to-child binding did not work properly. [`d355f2c`](https://github.com/logue/vue-codemirror6/commit/d355f2c7de4437360fdbf021029506d6cce4adba) 177 | - Changed the implementation to call the extension directly with a function. [`98d338f`](https://github.com/logue/vue-codemirror6/commit/98d338f05a48922529f15f9d706f9614447ff1cb) 178 | - The initial value of linter is set to simple undefined. [`6cec8b0`](https://github.com/logue/vue-codemirror6/commit/6cec8b000ce18a44a4077c375285a45df2993931) 179 | 180 | #### [0.5.2](https://github.com/logue/vue-codemirror6/compare/0.5.1...0.5.2) 181 | 182 | > 30 May 2022 183 | 184 | - Simplify extension processing. [`cd875ff`](https://github.com/logue/vue-codemirror6/commit/cd875ffdeffa072c6abb5b4e3964298bd490b582) 185 | - Since lintGutter is displayed even for components for which linter is not specified, lintGutter is made an option. [`ab16cbb`](https://github.com/logue/vue-codemirror6/commit/ab16cbb1472a5b29c572279a303a4baa593f4eb3) 186 | - Fixed innerText is undefined error. [`6466784`](https://github.com/logue/vue-codemirror6/commit/64667849779d0511a472cf8d116ecef8e51f1db8) 187 | 188 | #### [0.5.1](https://github.com/logue/vue-codemirror6/compare/0.5.0...0.5.1) 189 | 190 | > 28 May 2022 191 | 192 | - Fixed linter bug. [`03013b4`](https://github.com/logue/vue-codemirror6/commit/03013b44ce89ba455acafaceac68b2084fe364cf) 193 | 194 | #### [0.5.0](https://github.com/logue/vue-codemirror6/compare/0.3.7...0.5.0) 195 | 196 | > 27 May 2022 197 | 198 | - The output program is compatible with both Vue2 and Vue3.☺ [`7571423`](https://github.com/logue/vue-codemirror6/commit/7571423b0b4f6150300b184b3fc765cb3bf2e21b) 199 | 200 | #### [0.3.7](https://github.com/logue/vue-codemirror6/compare/0.3.6...0.3.7) 201 | 202 | > 26 May 2022 203 | 204 | - Change the formatting settings. [`42188a5`](https://github.com/logue/vue-codemirror6/commit/42188a5de4ddf1163789cdc2fbd255ed19487fad) 205 | - Update documents. [`5a78106`](https://github.com/logue/vue-codemirror6/commit/5a7810681c786020afdac8f5d8da8d15f8ad802c) 206 | - Update docs. [`15d7f8e`](https://github.com/logue/vue-codemirror6/commit/15d7f8e7e1edda995420485160cb9cfd4980fe74) 207 | 208 | #### [0.3.6](https://github.com/logue/vue-codemirror6/compare/0.3.2...0.3.6) 209 | 210 | > 19 May 2022 211 | 212 | - Rename serve.vue to DemoPage.vue. [`3ba582b`](https://github.com/logue/vue-codemirror6/commit/3ba582b6fa24259b49d1f123b79db185ab7e077a) 213 | - Fixed an issue where CodeMirror may not work properly during initial display. [`8092b64`](https://github.com/logue/vue-codemirror6/commit/8092b64039e9a3340624ca4e347151609c933a12) 214 | - Add basic and tab props. [`54eace6`](https://github.com/logue/vue-codemirror6/commit/54eace641678967163fdf93d051181f701167e0e) 215 | 216 | #### [0.3.2](https://github.com/logue/vue-codemirror6/compare/0.3.0...0.3.2) 217 | 218 | > 6 April 2022 219 | 220 | - When building with vue3, it doesn't work with vue2, so build with vue2. [`87be702`](https://github.com/logue/vue-codemirror6/commit/87be702b278df9cf0f5b26bb8307f7a6d592fd73) 221 | - Rewrite the wrapper part. [`1e77661`](https://github.com/logue/vue-codemirror6/commit/1e776619bb5f711a789bbe758d10142725de39aa) 222 | - Update sample. [`f7ed0a5`](https://github.com/logue/vue-codemirror6/commit/f7ed0a507175b6c0d79cf6be55fbd317c5c999b6) 223 | 224 | #### [0.3.0](https://github.com/logue/vue-codemirror6/compare/0.1.7...0.3.0) 225 | 226 | > 29 March 2022 227 | 228 | #### [0.1.7](https://github.com/logue/vue-codemirror6/compare/0.1.6...0.1.7) 229 | 230 | > 26 May 2022 231 | 232 | - Change Lint settings. [`89558c1`](https://github.com/logue/vue-codemirror6/commit/89558c1ad3a11bc5bd4ee55a11d6bd58022c99eb) 233 | 234 | #### [0.1.6](https://github.com/logue/vue-codemirror6/compare/0.1.2...0.1.6) 235 | 236 | > 19 May 2022 237 | 238 | - Follow changes in the master branch [`d7a9e3f`](https://github.com/logue/vue-codemirror6/commit/d7a9e3f9cfd7580bb78c29f96e1f2c84d77d5801) 239 | - Follow Vue3 version. [`1867c6c`](https://github.com/logue/vue-codemirror6/commit/1867c6c18fdc10f331e1dace37c5773242069150) 240 | - Since past values may be included, nextTick processing was added to onMouted. [`dac2bdd`](https://github.com/logue/vue-codemirror6/commit/dac2bdd1ea935141ed25b93c9882f6ecc871099e) 241 | 242 | #### [0.1.2](https://github.com/logue/vue-codemirror6/compare/0.1.0...0.1.2) 243 | 244 | > 6 April 2022 245 | 246 | - Squashed commit of the following: [`db4abdd`](https://github.com/logue/vue-codemirror6/commit/db4abdddda262a7fa4f69f028b423d13442cec68) 247 | - When building with vue3, it doesn't work with vue2, so build with vue2. [`87be702`](https://github.com/logue/vue-codemirror6/commit/87be702b278df9cf0f5b26bb8307f7a6d592fd73) 248 | - Rewrite the wrapper part. [`1e77661`](https://github.com/logue/vue-codemirror6/commit/1e776619bb5f711a789bbe758d10142725de39aa) 249 | 250 | #### [0.1.0](https://github.com/logue/vue-codemirror6/compare/0.0.6...0.1.0) 251 | 252 | > 18 March 2022 253 | 254 | - Update Samples. (run `yarn run dev`) [`f658c65`](https://github.com/logue/vue-codemirror6/commit/f658c654f2910a133914f2fa0fdc8206dadb7232) 255 | - Fixed IME probrem. [`7a42e63`](https://github.com/logue/vue-codemirror6/commit/7a42e63f75748039ebb082732fe46cd7967f32d7) 256 | 257 | #### [0.0.6](https://github.com/logue/vue-codemirror6/compare/0.0.5...0.0.6) 258 | 259 | > 17 March 2022 260 | 261 | - Fixed d.ts file destination. [`2cd9cdf`](https://github.com/logue/vue-codemirror6/commit/2cd9cdf3f0658d62ae434600e4e8cda14c407a47) 262 | 263 | #### [0.0.5](https://github.com/logue/vue-codemirror6/compare/0.0.4...0.0.5) 264 | 265 | > 17 March 2022 266 | 267 | - Fixed a bug that the cursor position may be at the top when entering a key. [`e7f5407`](https://github.com/logue/vue-codemirror6/commit/e7f5407c76185cc805c10fc0366c5e6dff5f44f6) 268 | 269 | #### [0.0.4](https://github.com/logue/vue-codemirror6/compare/0.0.2...0.0.4) 270 | 271 | > 16 February 2022 272 | 273 | - Update demo code. [`599fa5f`](https://github.com/logue/vue-codemirror6/commit/599fa5f4d87cc7783c98f56a9436a314b368fb64) 274 | - Removed code that depends on other libraries from the output code. [`7d9e383`](https://github.com/logue/vue-codemirror6/commit/7d9e383c17f31ed8721f94711e7cd1dc595f9e07) 275 | - Add demo code. [`3e06ef7`](https://github.com/logue/vue-codemirror6/commit/3e06ef70f5ed30fce5a6cb72add2b41fea2745fe) 276 | 277 | #### [0.0.2](https://github.com/logue/vue-codemirror6/compare/0.0.1...0.0.2) 278 | 279 | > 14 February 2022 280 | 281 | - Update package.json. Fixed typing settings. [`12ead23`](https://github.com/logue/vue-codemirror6/commit/12ead23c53c0a5ebc5a68c3bee7b192b587efafc) 282 | 283 | #### 0.0.1 284 | 285 | > 10 February 2022 286 | 287 | - Initial commit. [`b410884`](https://github.com/logue/vue-codemirror6/commit/b41088482f82615e9380e6231a59a39387d172a2) 288 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2025 Masashi Yoshikawa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-codemirror6 2 | 3 |

4 | logo 5 |

6 | 7 | [![jsdelivr CDN](https://data.jsdelivr.com/v1/package/npm/vue-codemirror6/badge)](https://www.jsdelivr.com/package/npm/vue-codemirror6) 8 | [![NPM Downloads](https://img.shields.io/npm/dm/vue-codemirror6.svg?style=flat)](https://www.npmjs.com/package/vue-codemirror6) 9 | [![Open in unpkg](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/vue-codemirror6/file/README.md) 10 | [![npm version](https://img.shields.io/npm/v/vue-codemirror6.svg)](https://www.npmjs.com/package/vue-codemirror6) 11 | [![Open in Gitpod](https://shields.io/badge/Open%20in-Gitpod-green?logo=Gitpod)](https://gitpod.io/#https://github.com/logue/vue-codemirror6) 12 | [![Twitter Follow](https://img.shields.io/twitter/follow/logue256?style=plastic)](https://twitter.com/logue256) 13 | 14 | A component for using [CodeMirror6](https://codemirror.net/6/) with Vue. This component works in both Vue2 and Vue3. 15 | 16 | - [CHANGELOG](./CHANGELOG.md) 17 | 18 | ## Usage 19 | 20 | ```sh 21 | yarn add vue-codemirror6 codemirror 22 | ``` 23 | 24 | For Vue 2.7 or below, [@vue/composition-api](https://www.npmjs.com/package/@vue/composition-api) is required separately. 25 | 26 | ```sh 27 | yarn add vue-codemirror6 @vue/composition-api 28 | ``` 29 | 30 | This component can handle bidirectional binding by `v-model` like a general Vue component. 31 | 32 | ## Props 33 | 34 | | Props | Type | Information | 35 | | ------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 36 | | model-value | string \| Text | Text value. (Not `value`) | 37 | | basic | boolean | Use [basicSetup](https://codemirror.net/docs/ref/#codemirror.basicSetup). | 38 | | minimal | boolean | Use [miniSetup](https://codemirror.net/docs/ref/#codemirror.minimalSetup). If a `basic` prop is also specified, that setting will take precedence. | 39 | | dark | boolean | Toggle Darkmode. | 40 | | placeholder | string | Add placeholder text (or HTML DOM) when blank | 41 | | wrap | boolean | Line text wrapping. see [lineWrapping](https://codemirror.net/6/docs/ref/#view.EditorView.lineWrapping). | 42 | | tab | boolean | Enables tab indentation. | 43 | | allow-multiple-selections | boolean | Allow Multiple Selection. See [allowMultipleSelections](https://codemirror.net/docs/ref/#state.EditorState^allowMultipleSelections) | 44 | | tab-size | number | Configures the tab size to use in this state. | 45 | | line-separator | string | Set line break (separetor) char. (Default is `\n`.) | 46 | | theme | { [selector: string]: StyleSpec } | Specify the theme. For example, if you use [@codemirror/theme-one-dark](https://github.com/codemirror/theme-one-dark), import `oneDark` and put it in this prop. | 47 | | readonly | boolean | Makes the cursor visible or you can drag the text but not edit the value. | 48 | | disabled | boolean | This is the reversed value of the CodeMirror editable. Similar to `readonly`, but setting this value to true disables dragging. | 49 | | lang | LanguageSupport | The language you want to have syntax highlighting. see | 50 | | phrases | Record<string, string> | Specify here if you want to make the displayed character string multilingual. see | 51 | | extensions | Extension[] | Includes enhancements to extend CodeMirror. | 52 | | linter | LintSource | Set Linter. Enter a linter (eg `esLint([arbitrary rule])` function for `@codemirror/lang-javascript`, `jsonParseLinter()`function for`@codemirror/json`). See the sources for various language libraries for more information. | 53 | | linterConfig | Object | see | 54 | | forceLinting | boolean | see | 55 | | gutter | boolean | Display 🔴 on the line where there was an error when `linter` was specified. It will not work if `linter` is not specified. | 56 | | gutterConfig | Object | see | 57 | | tag | string | HTML tags used in the component. (Default is `div` tag.) | 58 | | scrollIntoView | boolean | Allows an external update to scroll the form. (Default is `true`) | 59 | 60 | ⚠ Notice: `lang` and `linter` can also be set together in `extensions`. These are separated for compatibility with previous versions of CodeMirror settings and for typing props. 61 | 62 | ### Supported Languages 63 | 64 | #### Official 65 | 66 | - [`@codemirror/lang-angular`](https://www.npmjs.com/package/@codemirror/lang-angular) 67 | - [`@codemirror/lang-cpp`](https://www.npmjs.com/package/@codemirror/lang-cpp) 68 | - [`@codemirror/lang-css`](https://www.npmjs.com/package/@codemirror/lang-css) 69 | - [`@codemirror/lang-html`](https://www.npmjs.com/package/@codemirror/lang-html) 70 | - [`@codemirror/lang-java`](https://www.npmjs.com/package/@codemirror/lang-java) 71 | - [`@codemirror/lang-javascript`](https://www.npmjs.com/package/@codemirror/lang-javascript) 72 | - [`@codemirror/lang-json`](https://www.npmjs.com/package/@codemirror/lang-json) 73 | - [`@codemirror/lang-lezer`](https://www.npmjs.com/package/@codemirror/lang-lezer) 74 | - [`@codemirror/lang-markdown`](https://www.npmjs.com/package/@codemirror/lang-markdown) 75 | - [`@codemirror/lang-php`](https://www.npmjs.com/package/@codemirror/lang-php) 76 | - [`@codemirror/lang-python`](https://www.npmjs.com/package/@codemirror/lang-python) 77 | - [`@codemirror/lang-rust`](https://www.npmjs.com/package/@codemirror/lang-rust) 78 | - [`@codemirror/lang-sql`](https://www.npmjs.com/package/@codemirror/lang-sql) 79 | - [`@codemirror/lang-vue`](https://www.npmjs.com/package/@codemirror/lang-vue) 80 | - [`@codemirror/lang-west`](https://www.npmjs.com/package/@codemirror/lang-west) 81 | - [`@codemirror/lang-xml`](https://www.npmjs.com/package/@codemirror/lang-xml) 82 | 83 | ### Unofficial 84 | 85 | - [`@phoenix-plugin-registry/petetnt.brackets-codemirror-fortran`](https://www.npmjs.com/package/@phoenix-plugin-registry/petetnt.brackets-codemirror-fortran) 86 | - [`@phoenix-plugin-registry/petetnt.brackets-codemirror-go`](https://www.npmjs.com/package/@phoenix-plugin-registry/petetnt.brackets-codemirror-go) 87 | - [`@acarl005/lang-sql`](https://www.npmjs.com/package/@acarl005/lang-sql) 88 | - [`@ark-us/codemirror-lang-taylor`](https://www.npmjs.com/package/@ark-us/codemirror-lang-taylor) 89 | - [`@formulavize/lang-fiz`](https://www.npmjs.com/package/@formulavize/lang-fiz) 90 | - [`@gravitywiz/codemirror-lang-gfcalc`](https://www.npmjs.com/package/@gravitywiz/codemirror-lang-gfcalc) 91 | - [`@nextjournal/lang-clojure`](https://www.npmjs.com/package/@nextjournal/lang-clojure) 92 | - [`@plutojl/lang-julia`](https://www.npmjs.com/package/@plutojl/lang-julia) 93 | - [`@polybase/codemirror-lang-javascript`](https://www.npmjs.com/package/@polybase/codemirror-lang-javascript) -[`@replit/codemirror-lang-nix`](https://www.npmjs.com/package/@replit/codemirror-lang-nix) 94 | - [`@replit/codemirror-lang-csharp`](https://www.npmjs.com/package/@replit/codemirror-lang-csharp) 95 | - [`@replit/codemirror-lang-solidity`](https://www.npmjs.com/package/@replit/codemirror-lang-solidity) 96 | - [`@replit/codemirror-lang-svelte`](https://www.npmjs.com/package/@replit/codemirror-lang-svelte) 97 | - [`@zhijiu/lang-sql`](https://www.npmjs.com/package/@zhijiu/lang-sql) 98 | - [`codemirror-lang-bool`](https://www.npmjs.com/package/codemirror-lang-bool) 99 | - [`codemirror-lang-brainfuck`](https://www.npmjs.com/package/codemirror-lang-brainfuck) 100 | - [`codemirror-lang-cherry`](https://www.npmjs.com/package/codemirror-lang-cherry) 101 | - [`codemirror-lang-chordpro`](https://www.npmjs.com/package/codemirror-lang-chordpro) 102 | - [`codemirror-lang-circom`](https://www.npmjs.com/package/codemirror-lang-circom) 103 | - [`codemirror-lang-edn`](https://www.npmjs.com/package/codemirror-lang-edn) 104 | - [`codemirror-lang-ejs`](https://www.npmjs.com/package/codemirror-lang-ejs) 105 | - [`codemirror-lang-fsl`](https://www.npmjs.com/package/codemirror-lang-fsl) 106 | - [`codemirror-lang-gml`](https://www.npmjs.com/package/codemirror-lang-gml) 107 | - [`codemirror-lang-golfscript`](https://www.npmjs.com/package/codemirror-lang-golfscript) 108 | - [`codemirror-lang-homescript`](https://www.npmjs.com/package/codemirror-lang-homescript) 109 | - [`codemirror-lang-html-n8n`](https://www.npmjs.com/package/codemirror-lang-html-n8n) 110 | - [`codemirror-lang-inform7`](https://www.npmjs.com/package/codemirror-lang-inform7) 111 | - [`codemirror-lang-j`](https://www.npmjs.com/package/codemirror-lang-j) 112 | - [`codemirror-lang-janet`](https://www.npmjs.com/package/codemirror-lang-janet) 113 | - [`codemirror-lang-k`](https://www.npmjs.com/package/codemirror-lang-k) 114 | - [`codemirror-lang-karol`](https://www.npmjs.com/package/codemirror-lang-karol) 115 | - [`codemirror-lang-mermaid`](https://www.npmjs.com/package/codemirror-lang-mermaid) 116 | - [`codemirror-lang-n8n-expression`](https://www.npmjs.com/package/codemirror-lang-n8n-expression) 117 | - [`codemirror-lang-prolog`](https://www.npmjs.com/package/codemirror-lang-prolog) 118 | - [`codemirror-lang-qpen`](https://www.npmjs.com/package/codemirror-lang-qpen) 119 | - [`codemirror-lang-qtam`](https://www.npmjs.com/package/codemirror-lang-qtam) 120 | - [`codemirror-lang-r`](https://www.npmjs.com/package/codemirror-lang-r) 121 | - [`codemirror-lang-rome-ast`](https://www.npmjs.com/package/codemirror-lang-rome-ast) 122 | - [`codemirror-lang-rome`](https://www.npmjs.com/package/codemirror-lang-rome) 123 | - [`codemirror-lang-rush`](https://www.npmjs.com/package/codemirror-lang-rush) 124 | - [`codemirror-lang-scopescript`](https://www.npmjs.com/package/codemirror-lang-scopescript) 125 | - [`codemirror-lang-statement`](https://www.npmjs.com/package/codemirror-lang-statement) 126 | - [`gcode-lang-codemirror`](https://www.npmjs.com/package/gcode-lang-codemirror) 127 | - [`gmail-lang`](https://www.npmjs.com/package/gmail-lang) 128 | - [`lang-bqn`](https://www.npmjs.com/package/lang-bqn) 129 | - [`lang-clojure`](https://www.npmjs.com/package/lang-clojure) 130 | - [`lang-d`](https://www.npmjs.com/package/lang-d) 131 | - [`lang-feel`](https://www.npmjs.com/package/lang-feel) 132 | - [`lang-firestore`](https://www.npmjs.com/package/lang-firestore) 133 | 134 | ### Supported Themes 135 | 136 | - [`@codemirror/theme-one-dark`](https://github.com/codemirror/theme-one-dark) 137 | - [`upleveled/theme-vs-code-dark-plus`](https://github.com/upleveled/theme-vs-code-dark-plus) 138 | - [`codemirror6-bootstrap-theme`](https://github.com/logue/codemirror6-bootstrap-theme) 139 | 140 | ## Example 141 | 142 | Mark up as follows to make it work at a minimum. 143 | 144 | ```vue 145 | 148 | 149 | 165 | ``` 166 | 167 | ### Example using Slots 168 | 169 | The contents of the slot will overwrite the existing `v-model`. For this reason, it is recommended to use it when simply displaying with a `readonly` prop without using `v-model`. 170 | 171 | Also, insert a `
` tag to prevent the text in the slot from being automatically formatted.
172 | 
173 | ```vue
174 | 
183 | 
184 | 
202 | ```
203 | 
204 | ### Full Example
205 | 
206 | When using as a Markdown editor on [vite-vue3-ts-starter](https://github.com/logue/vite-vue3-ts-starter).
207 | 
208 | ```vue
209 | 
281 | 
282 | 
291 | ```
292 | 
293 | ## Events
294 | 
295 | | Event  | Description                                                                                                   |
296 | | ------ | ------------------------------------------------------------------------------------------------------------- |
297 | | ready  | When CodeMirror loaded.                                                                                       |
298 | | focus  | When focus changed.                                                                                           |
299 | | update | When CodeMirror state changed. Returns [ViewUpdate](https://codemirror.net/docs/ref/#view.ViewUpdate) object. |
300 | | change | Value changed. Returns [EditorState](https://codemirror.net/docs/ref/#state.EditorState)                      |
301 | 
302 | ## Parameter / Function
303 | 
304 | ```vue
305 | 
315 | 
318 | ```
319 | 
320 | | Function / Parameter | Description                                                                                         |
321 | | -------------------- | --------------------------------------------------------------------------------------------------- |
322 | | view                 | Get and set [EditorView](https://codemirror.net/docs/ref/#view.EditorView).                         |
323 | | selection            | Get and set the [EditorSelection](https://codemirror.net/docs/ref/#state.EditorSelection) instance. |
324 | | cursor               | Get and set the [cursor](https://codemirror.net/docs/ref/#state.EditorSelection^cursor) location.   |
325 | | json                 | Get and set state to a JSON-serializable object.                                                    |
326 | | focus                | Get and set [focus](https://codemirror.net/docs/ref/#view.EditorView.focus).                        |
327 | | lint()               | Force run linter (Only if `linter` prop is specified)                                               |
328 | | forceReconfigure()   | Re register all extensions.                                                                         |
329 | 
330 | ### CodeMirror5 backward compatible functions
331 | 
332 | The instructions below are compatible methods for those familiar with [codemirror5](https://codemirror.net/5/).
333 | Since the above method is usually sufficient, its **active use is not recommended**.
334 | 
335 | | Function                                                            | Description                                                                                      |
336 | | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
337 | | getRange(from?: number, to?: number)                                | Get the text between the given points in the editor.                                             |
338 | | getLine(number: number)                                             | Get the content of line.                                                                         |
339 | | lineCount()                                                         | Get the number of lines in the editor.                                                           |
340 | | getCursor()                                                         | Retrieve one end of the primary selection.                                                       |
341 | | listSelections()                                                    | Retrieves a list of all current selections.                                                      |
342 | | getSelection()                                                      | Get the currently selected code.                                                                 |
343 | | getSelections()                                                     | The length of the given array should be the same as the number of active selections.             |
344 | | somethingSelected()                                                 | Return true if any text is selected.                                                             |
345 | | replaceRange(replacement: string \| Text, from: number, to: number) | Replace the part of the document between from and to with the given string.                      |
346 | | replaceSelection(replacement: string \| Text)                       | Replace the selection(s) with the given string.                                                  |
347 | | setCursor(position: number)                                         | Set the cursor position.                                                                         |
348 | | setSelection(anchor: number, head?: number)                         | Set a single selection range.                                                                    |
349 | | setSelections(ranges: readonly SelectionRange[], primary?: number)  | Sets a new set of selections.                                                                    |
350 | | extendSelectionsBy(f: Function)                                     | Applies the given function to all existing selections, and calls extendSelections on the result. |
351 | 
352 | ## Recommendations
353 | 
354 | Since CodeMirror has a relatively large capacity, when using [vite](https://vitejs.dev), it is recommended to set it to output as a separate file using the [`manualChunks`](https://vitejs.dev/guide/build.html#chunking-strategy) option at build time as shown below.
355 | 
356 | ```ts
357 | const config: UserConfig = {
358 |   // ...
359 |   build: {
360 |     rollupOptions: {
361 |       output: {
362 |         manualChunks: {
363 |           // ...
364 |           codemirror: ['vue-codemirror6'],
365 |           'codemirror-lang': [
366 |             // Add the following as needed.
367 |             '@codemirror/lang-html',
368 |             '@codemirror/lang-javascript',
369 |             '@codemirror/lang-markdown',
370 |           ],
371 |           // ...
372 |         },
373 |       },
374 |     },
375 |   },
376 |   // ...
377 | };
378 | ```
379 | 
380 | ## LICENSE
381 | 
382 | ©2022-2025 by Logue.
383 | Licensed under the [MIT License](LICENSE).
384 | 


--------------------------------------------------------------------------------
/env.d.ts:
--------------------------------------------------------------------------------
 1 | /* eslint-disable */
 2 | /// 
 3 | 
 4 | declare module '*.vue' {
 5 |   import Vue from 'vue';
 6 |   export default Vue;
 7 | }
 8 | interface ImportMetaEnv {
 9 |   // see https://vitejs.dev/guide/env-and-mode.html#env-files
10 |   // add .env variables.
11 | }
12 | 
13 | interface ImportMeta {
14 |   readonly env: ImportMetaEnv;
15 | }
16 | 


--------------------------------------------------------------------------------
/eslint.config.ts:
--------------------------------------------------------------------------------
  1 | import configPrettier from '@vue/eslint-config-prettier';
  2 | import {
  3 |   defineConfigWithVueTs,
  4 |   vueTsConfigs,
  5 | } from '@vue/eslint-config-typescript';
  6 | 
  7 | // @ts-ignore
  8 | import pluginImport from 'eslint-plugin-import';
  9 | import pluginVue from 'eslint-plugin-vue';
 10 | import pluginVueA11y from 'eslint-plugin-vuejs-accessibility';
 11 | 
 12 | // To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
 13 | // import { configureVueProject } from '@vue/eslint-config-typescript'
 14 | // configureVueProject({ scriptLangs: ['ts', 'tsx'] })
 15 | // More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
 16 | 
 17 | /**
 18 |  * ESLint Config
 19 |  */
 20 | export default defineConfigWithVueTs(
 21 |   {
 22 |     name: 'app/files-to-lint',
 23 |     files: ['**/*.{ts,mts,tsx,vue}'],
 24 |   },
 25 |   {
 26 |     name: 'app/files-to-ignore',
 27 |     ignores: [
 28 |       '.vscode/',
 29 |       '.yarn/',
 30 |       '**/dist/**',
 31 |       '**/dist-ssr/**',
 32 |       '**/coverage/**',
 33 |       'eslint.config.js',
 34 |       'pnpm-lock.yaml',
 35 |       'playwright-report',
 36 |       'test-results',
 37 |       'public/',
 38 |       'src/**/*.generated.*',
 39 |       'docs',
 40 |     ],
 41 |   },
 42 |   pluginVue.configs['flat/recommended'],
 43 |   ...pluginVueA11y.configs['flat/recommended'],
 44 |   vueTsConfigs.recommended,
 45 |   {
 46 |     plugins: {
 47 |       import: pluginImport,
 48 |     },
 49 | 
 50 |     settings: {
 51 |       // This will do the trick
 52 |       'import/parsers': {
 53 |         espree: ['.js', '.cjs', '.mjs', '.jsx'],
 54 |         '@typescript-eslint/parser': ['.ts', '.tsx'],
 55 |         'vue-eslint-parser': ['.vue'],
 56 |       },
 57 |       'import/resolver': {
 58 |         typescript: true,
 59 |         node: true,
 60 |         'eslint-import-resolver-custom-alias': {
 61 |           alias: {
 62 |             '@': './src',
 63 |             '~': './node_modules',
 64 |           },
 65 |           extensions: ['.js', '.ts', '.jsx', '.tsx', '.vue'],
 66 |         },
 67 |       },
 68 |       vite: {
 69 |         configPath: './vite.config.ts',
 70 |       },
 71 |     },
 72 |     rules: {
 73 |       // ...importPlugin.configs["recommended"].rules,
 74 |       'no-unused-vars': 'warn',
 75 |       // const lines: string[] = []; style
 76 |       '@typescript-eslint/array-type': [
 77 |         'error',
 78 |         {
 79 |           default: 'array',
 80 |         },
 81 |       ],
 82 |       // Enable @ts-ignore etc.
 83 |       '@typescript-eslint/ban-ts-comment': 'off',
 84 |       // Left-hand side style
 85 |       '@typescript-eslint/consistent-generic-constructors': [
 86 |         'error',
 87 |         'type-annotation',
 88 |       ],
 89 |       // Enable import sort order, see bellow.
 90 |       '@typescript-eslint/consistent-type-imports': [
 91 |         'off',
 92 |         {
 93 |           prefer: 'type-imports',
 94 |         },
 95 |       ],
 96 |       // Fix for pinia
 97 |       '@typescript-eslint/explicit-function-return-type': 'off',
 98 |       '@typescript-eslint/no-explicit-any': 'off',
 99 |       // Exclude variables with leading underscores
100 |       '@typescript-eslint/no-unused-vars': [
101 |         'error',
102 |         {
103 |           args: 'all',
104 |           argsIgnorePattern: '^_',
105 |           caughtErrors: 'all',
106 |           caughtErrorsIgnorePattern: '^_',
107 |           destructuredArrayIgnorePattern: '^_',
108 |           varsIgnorePattern: '^_',
109 |           ignoreRestSiblings: true,
110 |         },
111 |       ],
112 |       // Fix for vite import.meta.env
113 |       '@typescript-eslint/strict-boolean-expressions': 'off',
114 |       // Fix for vite env.d.ts.
115 |       '@typescript-eslint/triple-slash-reference': 'off',
116 |       // Fix for Vue setup style
117 |       'import/default': 'off',
118 |       // Fix for Vue setup style
119 |       'import/no-default-export': 'off',
120 |       // Sort Import Order.
121 |       // see https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md#importorder-enforce-a-convention-in-module-import-order
122 |       'import/order': [
123 |         'error',
124 |         {
125 |           groups: [
126 |             'builtin',
127 |             'external',
128 |             'parent',
129 |             'sibling',
130 |             'index',
131 |             'object',
132 |             'type',
133 |           ],
134 |           pathGroups: [
135 |             // Vue Core
136 |             {
137 |               pattern:
138 |                 '{vue,vue-router,vuex,@/store,vue-i18n,pinia,vite,vitest,vitest/**,@vitejs/**,@vue/**}',
139 |               group: 'external',
140 |               position: 'before',
141 |             },
142 |             // Internal Codes
143 |             {
144 |               pattern: '{@/**}',
145 |               group: 'internal',
146 |               position: 'before',
147 |             },
148 |           ],
149 |           pathGroupsExcludedImportTypes: ['builtin'],
150 |           alphabetize: {
151 |             order: 'asc',
152 |           },
153 |           'newlines-between': 'always',
154 |         },
155 |       ],
156 |       // A tag with no content should be written like 
. 157 | 'vue/html-self-closing': [ 158 | 'error', 159 | { 160 | html: { 161 | void: 'always', 162 | }, 163 | }, 164 | ], 165 | // Mitigate non-multiword component name errors to warnings. 166 | 'vue/multi-word-component-names': 'warn', 167 | // allow model-value 168 | 'vue/no-deprecated-model-definition': [ 169 | 'error', 170 | { 171 | allowVue3Compat: true, 172 | }, 173 | ], 174 | }, 175 | }, 176 | configPrettier 177 | ); 178 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Vue CodeMirror6 Demo 9 | 10 | 14 | 22 | 23 | 24 | 25 |
26 | We're sorry but this site doesn't work properly without JavaScript 27 | enabled. Please enable it to continue. 28 |
29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/package.json", 3 | "name": "vue-codemirror6", 4 | "version": "1.3.17", 5 | "license": "MIT", 6 | "description": "CodeMirror6 Component for vue2 and vue3.", 7 | "keywords": [ 8 | "vuejs", 9 | "vue", 10 | "vue-components", 11 | "vue-codemirror", 12 | "code-editor", 13 | "text-editor", 14 | "vue2", 15 | "vue3", 16 | "web-editor", 17 | "vue-plugin", 18 | "vue-component", 19 | "codemirror-editor", 20 | "vue-resource", 21 | "codemirror6" 22 | ], 23 | "type": "module", 24 | "author": { 25 | "name": "Logue", 26 | "email": "logue@hotmail.co.jp", 27 | "url": "https://logue.dev/" 28 | }, 29 | "homepage": "https://github.com/logue/vue-codemirror6", 30 | "repository": { 31 | "type": "git", 32 | "url": "git+ssh://git@github.com/logue/vue-codemirror6.git" 33 | }, 34 | "bugs": { 35 | "url": "https://github.com/logue/vue-codemirror6/issues" 36 | }, 37 | "main": "dist/index.umd.js", 38 | "module": "dist/index.es.js", 39 | "jsdelivr": "dist/index.iife.js", 40 | "unpkg": "dist/index.iife.js", 41 | "types": "dist/index.d.ts", 42 | "exports": { 43 | ".": { 44 | "import": "./dist/index.es.js", 45 | "types": "./dist/index.d.ts" 46 | } 47 | }, 48 | "files": [ 49 | "CHANGELOG.md", 50 | "/dist", 51 | "./src/components/CodeMirror.ts" 52 | ], 53 | "browser": { 54 | "./sfc": "./src/components/CodeMirror.ts" 55 | }, 56 | "engines": { 57 | "pnpm": ">=10.3.0" 58 | }, 59 | "packageManager": "pnpm@10.11.0", 60 | "sideEffects": false, 61 | "scripts": { 62 | "dev": "vite", 63 | "clean": "rimraf node_modules/.vite", 64 | "build": "run-p type-check \"build-only {@}\" --", 65 | "build:analyze": "vite build --mode=analyze", 66 | "build:clean": "rimraf dist docs", 67 | "build:docs": "vite build --mode=docs", 68 | "lint": "eslint . --fix --cache --cache-location ./node_modules/.vite/vite-plugin-eslint && prettier . --write", 69 | "preview": "vite preview --mode=docs", 70 | "build-only": "vite build", 71 | "type-check": "vue-tsc --declaration --emitDeclarationOnly", 72 | "prepare": "husky", 73 | "version": "auto-changelog -p && git add CHANGELOG.md" 74 | }, 75 | "dependencies": { 76 | "@codemirror/autocomplete": "^6.18.6", 77 | "@codemirror/commands": "^6.8.1", 78 | "@codemirror/language": "^6.11.0", 79 | "@codemirror/lint": "^6.8.5", 80 | "@codemirror/search": "^6.5.11", 81 | "@codemirror/state": "^6.5.2", 82 | "@codemirror/view": "^6.37.1", 83 | "codemirror": "^6.0.1", 84 | "style-mod": "^4.1.2", 85 | "vue-demi": "latest" 86 | }, 87 | "peerDependencies": { 88 | "vue": "^2.7.14 || ^3.4" 89 | }, 90 | "devDependencies": { 91 | "@codemirror/lang-javascript": "^6.2.4", 92 | "@codemirror/lang-json": "^6.0.1", 93 | "@codemirror/lang-markdown": "^6.3.2", 94 | "@codemirror/lang-vue": "^0.1.3", 95 | "@tsconfig/node-lts": "^22.0.1", 96 | "@types/node": "^22.15.29", 97 | "@vitejs/plugin-vue": "^5.2.4", 98 | "@vue/compiler-sfc": "^3.5.16", 99 | "@vue/eslint-config-prettier": "^10.2.0", 100 | "@vue/eslint-config-typescript": "^14.5.0", 101 | "@vue/test-utils": "^2.4.6", 102 | "@vue/tsconfig": "^0.7.0", 103 | "@vueuse/core": "^13.3.0", 104 | "bootstrap": "^5.3.6", 105 | "eslint": "^9.28.0", 106 | "eslint-import-resolver-custom-alias": "^1.3.2", 107 | "eslint-import-resolver-typescript": "^4.4.2", 108 | "eslint-linter-browserify": "^9.28.0", 109 | "eslint-plugin-import": "^2.31.0", 110 | "eslint-plugin-playwright": "^2.2.0", 111 | "eslint-plugin-vue": "^10.1.0", 112 | "eslint-plugin-vuejs-accessibility": "^2.4.1", 113 | "husky": "^9.1.7", 114 | "jiti": "^2.4.2", 115 | "lint-staged": "^16.1.0", 116 | "npm-run-all2": "^8.0.4", 117 | "prettier": "^3.5.3", 118 | "rimraf": "^6.0.1", 119 | "rollup-plugin-visualizer": "^6.0.1", 120 | "sass": "^1.89.1", 121 | "supports-color": "^10.0.0", 122 | "typescript": "^5.8.3", 123 | "typescript-eslint": "^8.33.0", 124 | "vite": "^6.3.5", 125 | "vite-plugin-banner": "^0.8.1", 126 | "vite-plugin-checker": "^0.9.3", 127 | "vite-plugin-dts": "^4.5.4", 128 | "vue": "^3.5.16", 129 | "vue-eslint-parser": "^10.1.3", 130 | "vue-markdown-wasm": "^0.5.1", 131 | "vue-tsc": "^2.2.10" 132 | }, 133 | "husky": { 134 | "hooks": { 135 | "pre-commit": "lint-staged" 136 | } 137 | }, 138 | "lint-staged": { 139 | "*.{js,ts,json,htm,html,vue}": "eslint --fix --cache --cache-location ./node_modules/.vite/vite-plugin-eslint", 140 | "*": "prettier -w -u" 141 | }, 142 | "resolutions": { 143 | "json5": ">=2.2.3", 144 | "yaml": ">=2.6.0" 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - esbuild 3 | - unrs-resolver 4 | - vue-demi 5 | -------------------------------------------------------------------------------- /public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /src-docs/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 73 | 74 | 79 | -------------------------------------------------------------------------------- /src-docs/DemoPage.vue: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 216 | -------------------------------------------------------------------------------- /src-docs/components/LinterAndCrossBindingDemo.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 47 | 118 | -------------------------------------------------------------------------------- /src-docs/components/MarkdownDemo.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 41 | 42 | 95 | -------------------------------------------------------------------------------- /src-docs/components/ReadonlyAndDisabledDemo.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 49 | -------------------------------------------------------------------------------- /src-docs/components/SlotDemo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 30 | -------------------------------------------------------------------------------- /src-docs/components/ToggleTheme.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 34 | -------------------------------------------------------------------------------- /src-docs/main.ts: -------------------------------------------------------------------------------- 1 | import './style.scss'; 2 | 3 | /** Demo Code */ 4 | import { createApp } from 'vue'; 5 | 6 | import App from './App.vue'; 7 | 8 | createApp(App).mount('#app'); 9 | -------------------------------------------------------------------------------- /src-docs/style.scss: -------------------------------------------------------------------------------- 1 | // 1. Include functions first (so you can manipulate colors, SVGs, calc, etc) 2 | @import '~/bootstrap/scss/functions'; 3 | 4 | // 2. Include any default variable overrides here 5 | 6 | // 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets) 7 | @import '~/bootstrap/scss/variables'; 8 | @import '~/bootstrap/scss/variables-dark'; 9 | 10 | // 4. Include any default map overrides here 11 | 12 | // 5. Include remainder of required parts 13 | @import '~/bootstrap/scss/maps'; 14 | @import '~/bootstrap/scss/mixins'; 15 | @import '~/bootstrap/scss/root'; 16 | 17 | // 6. Optionally include any other parts as needed 18 | @import '~/bootstrap/scss/utilities'; 19 | @import '~/bootstrap/scss/reboot'; 20 | // @import '~/bootstrap/scss/type'; 21 | @import '~/bootstrap/scss/forms'; 22 | // @import '~/bootstrap/scss/dropdown'; 23 | @import '~/bootstrap/scss/alert'; 24 | @import '~/bootstrap/scss/nav'; 25 | @import '~/bootstrap/scss/navbar'; 26 | @import '~/bootstrap/scss/containers'; 27 | @import '~/bootstrap/scss/grid'; 28 | @import '~/bootstrap/scss/helpers'; 29 | 30 | // 7. Optionally include utilities API last to generate classes based on the Sass map in `_utilities.scss` 31 | @import '~/bootstrap/scss/utilities/api'; 32 | 33 | // 8. Add additional custom code here 34 | .vue-codemirror * { 35 | font-family: var(--bs-font-monospace); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/CodeMirror.ts: -------------------------------------------------------------------------------- 1 | import { indentWithTab } from '@codemirror/commands'; 2 | import { indentUnit, type LanguageSupport } from '@codemirror/language'; 3 | import { 4 | diagnosticCount as linterDiagnosticCount, 5 | forceLinting, 6 | linter, 7 | lintGutter, 8 | type Diagnostic, 9 | type LintSource, 10 | } from '@codemirror/lint'; 11 | import { 12 | Compartment, 13 | EditorSelection, 14 | EditorState, 15 | StateEffect, 16 | type Transaction, 17 | type Extension, 18 | type SelectionRange, 19 | type StateField, 20 | type Text, 21 | } from '@codemirror/state'; 22 | import { 23 | EditorView, 24 | keymap, 25 | placeholder, 26 | type ViewUpdate, 27 | } from '@codemirror/view'; 28 | import { basicSetup, minimalSetup } from 'codemirror'; 29 | import { 30 | computed, 31 | defineComponent, 32 | nextTick, 33 | onMounted, 34 | onUnmounted, 35 | ref, 36 | shallowRef, 37 | watch, 38 | type ComputedRef, 39 | type PropType, 40 | type Ref, 41 | type ShallowRef, 42 | type WritableComputedRef, 43 | } from 'vue-demi'; 44 | 45 | import type { StyleSpec } from 'style-mod'; 46 | 47 | import h, { slot } from '@/helpers/h-demi'; 48 | 49 | /** CodeMirror Component */ 50 | export default defineComponent({ 51 | /** Component Name */ 52 | name: 'CodeMirror', 53 | /** Model Definition */ 54 | model: { 55 | prop: 'modelValue', 56 | event: 'update:modelValue', 57 | }, 58 | /** Props Definition */ 59 | props: { 60 | /** Model value */ 61 | modelValue: { 62 | type: String as PropType, 63 | default: '', 64 | }, 65 | /** 66 | * Theme 67 | * 68 | * @see {@link https://codemirror.net/docs/ref/#view.EditorView^theme} 69 | */ 70 | theme: { 71 | type: Object as PropType>, 72 | default: () => { 73 | return {}; 74 | }, 75 | }, 76 | /** Dark Mode */ 77 | dark: { 78 | type: Boolean, 79 | default: false, 80 | }, 81 | /** 82 | * Use Basic Setup 83 | * 84 | * @see {@link https://codemirror.net/docs/ref/#codemirror.basicSetup} 85 | */ 86 | basic: { 87 | type: Boolean, 88 | default: false, 89 | }, 90 | /** 91 | * Use Minimal Setup (The basic setting has priority.) 92 | * 93 | * @see {@link https://codemirror.net/docs/ref/#codemirror.minimalSetup} 94 | */ 95 | minimal: { 96 | type: Boolean, 97 | default: false, 98 | }, 99 | /** 100 | * Placeholder 101 | * 102 | * @see {@link https://codemirror.net/docs/ref/#view.placeholder} 103 | */ 104 | placeholder: { 105 | type: String as PropType, 106 | default: undefined, 107 | }, 108 | /** 109 | * Line wrapping 110 | * 111 | * An extension that enables line wrapping in the editor (by setting CSS white-space to pre-wrap in the content). 112 | * 113 | * @see {@link https://codemirror.net/docs/ref/#view.EditorView%5ElineWrapping} 114 | */ 115 | wrap: { 116 | type: Boolean, 117 | default: false, 118 | }, 119 | /** 120 | * Allow tab key indent. 121 | * 122 | * @see {@link https://codemirror.net/examples/tab/} 123 | */ 124 | tab: { 125 | type: Boolean, 126 | default: false, 127 | }, 128 | /** 129 | * Tab character 130 | */ 131 | indentUnit: { 132 | type: String, 133 | default: undefined, 134 | }, 135 | /** 136 | * Allow Multiple Selection. 137 | * 138 | * @see {@link https://codemirror.net/docs/ref/#state.EditorState^allowMultipleSelections} 139 | */ 140 | allowMultipleSelections: { 141 | type: Boolean, 142 | default: false, 143 | }, 144 | /** 145 | * Tab size 146 | * 147 | * @see {@link https://codemirror.net/docs/ref/#state.EditorState^tabSize} 148 | */ 149 | tabSize: { 150 | type: Number, 151 | default: undefined, 152 | }, 153 | /** 154 | * Set line break (separetor) char. 155 | * 156 | * @see {@link https://codemirror.net/docs/ref/#state.EditorState^lineSeparator} 157 | */ 158 | lineSeparator: { 159 | type: String, 160 | default: undefined, 161 | }, 162 | /** 163 | * Readonly 164 | * 165 | * @see {@link https://codemirror.net/docs/ref/#state.EditorState^readOnly} 166 | */ 167 | readonly: { 168 | type: Boolean, 169 | default: false, 170 | }, 171 | /** 172 | * Disable input. 173 | * 174 | * This is the reversed value of the CodeMirror editable. 175 | * Similar to `readonly`, but setting this value to true disables dragging. 176 | * 177 | * @see {@link https://codemirror.net/docs/ref/#view.EditorView^editable} 178 | */ 179 | disabled: { 180 | type: Boolean, 181 | default: false, 182 | }, 183 | /** 184 | * Additional Extension 185 | * 186 | * @see {@link https://codemirror.net/docs/ref/#state.Extension} 187 | */ 188 | extensions: { 189 | type: Array as PropType, 190 | default: () => { 191 | return []; 192 | }, 193 | }, 194 | /** 195 | * Language Phreses 196 | * 197 | * @see {@link https://codemirror.net/examples/translate/} 198 | */ 199 | phrases: { 200 | type: Object as PropType>, 201 | default: undefined, 202 | }, 203 | /** 204 | * CodeMirror Language 205 | * 206 | * @see {@link https://codemirror.net/docs/ref/#language} 207 | */ 208 | lang: { 209 | type: Object as PropType, 210 | default: undefined, 211 | }, 212 | /** 213 | * CodeMirror Linter 214 | * 215 | * @see {@link https://codemirror.net/docs/ref/#lint.linter} 216 | */ 217 | linter: { 218 | type: Function as PropType, 219 | default: undefined, 220 | }, 221 | /** 222 | * Linter Config 223 | * 224 | * @see {@link https://codemirror.net/docs/ref/#lint.linter^config} 225 | */ 226 | linterConfig: { 227 | type: Object, 228 | default: () => { 229 | return {}; 230 | }, 231 | }, 232 | /** 233 | * Forces any linters configured to run when the editor is idle to run right away. 234 | * 235 | * @see {@link https://codemirror.net/docs/ref/#lint.forceLinting} 236 | */ 237 | forceLinting: { 238 | type: Boolean, 239 | default: false, 240 | }, 241 | /** 242 | * Show Linter Gutter 243 | * 244 | * An area to 🔴 the lines with errors will be displayed. 245 | * This feature is not enabled if `linter` is not specified. 246 | * 247 | * @see {@link https://codemirror.net/docs/ref/#lint.lintGutter} 248 | */ 249 | gutter: { 250 | type: Boolean, 251 | default: false, 252 | }, 253 | /** 254 | * Gutter Config 255 | * 256 | * @see {@link https://codemirror.net/docs/ref/#lint.lintGutter^config} 257 | */ 258 | gutterConfig: { 259 | type: Object, 260 | default: undefined, 261 | }, 262 | /** 263 | * Using tag 264 | */ 265 | tag: { 266 | type: String, 267 | default: 'div', 268 | }, 269 | /** 270 | * Allows an external update to scroll the form. 271 | * @see {@link https://codemirror.net/docs/ref/#state.TransactionSpec.scrollIntoView} 272 | */ 273 | scrollIntoView: { 274 | type: Boolean, 275 | default: true, 276 | }, 277 | }, 278 | /** Emits */ 279 | emits: { 280 | /** Model Update */ 281 | 'update:modelValue': (_value: string | Text = '') => true, 282 | /** CodeMirror ViewUpdate */ 283 | update: (_value: ViewUpdate) => true, 284 | /** CodeMirror onReady */ 285 | ready: (_value: { 286 | view: EditorView; 287 | state: EditorState; 288 | container: HTMLElement; 289 | }) => true, 290 | /** CodeMirror onFocus */ 291 | focus: (_value: boolean) => true, 292 | /** State Changed */ 293 | change: (_value: EditorState) => true, 294 | /** CodeMirror onDestroy */ 295 | destroy: () => true, 296 | }, 297 | /** 298 | * Setup 299 | * 300 | * @param props - Props 301 | * @param context - Context 302 | */ 303 | setup(props, context) { 304 | /** Editor DOM */ 305 | const editor: Ref = ref(); 306 | 307 | /** Internal value */ 308 | const doc: Ref = ref(props.modelValue); 309 | 310 | /** 311 | * CodeMirror Editor View 312 | * 313 | * @see {@link https://codemirror.net/docs/ref/#view.EditorView} 314 | */ 315 | const view: ShallowRef = shallowRef(new EditorView()); 316 | 317 | /** 318 | * Focus 319 | * 320 | * @see {@link https://codemirror.net/docs/ref/#view.EditorView.hasFocus} 321 | */ 322 | const focus: WritableComputedRef = computed({ 323 | get: () => view.value.hasFocus, 324 | set: f => { 325 | if (f) { 326 | view.value.focus(); 327 | } 328 | }, 329 | }); 330 | 331 | /** 332 | * Editor Selection 333 | * 334 | * @see {@link https://codemirror.net/docs/ref/#state.EditorSelection} 335 | */ 336 | const selection: WritableComputedRef = computed({ 337 | get: () => view.value.state.selection, 338 | set: s => view.value.dispatch({ selection: s }), 339 | }); 340 | 341 | /** Cursor Position */ 342 | const cursor: WritableComputedRef = computed({ 343 | get: () => view.value.state.selection.main.head, 344 | set: a => view.value.dispatch({ selection: { anchor: a } }), 345 | }); 346 | 347 | /** JSON */ 348 | const json: WritableComputedRef>> = computed( 349 | { 350 | get: () => view.value.state.toJSON(), 351 | set: j => view.value.setState(EditorState.fromJSON(j)), 352 | } 353 | ); 354 | 355 | /** Text length */ 356 | const length: Ref = ref(0); 357 | 358 | /** 359 | * Returns the number of active lint diagnostics in the given state. 360 | * 361 | * @see {@link https://codemirror.net/docs/ref/#lint.diagnosticCount} 362 | */ 363 | const diagnosticCount: Ref = ref(0); 364 | 365 | /** Get CodeMirror Extension */ 366 | const extensions: ComputedRef = computed(() => { 367 | // Synamic Reconfiguration 368 | // @see https://codemirror.net/examples/config/ 369 | const language = new Compartment(); 370 | const tabSize = new Compartment(); 371 | if (props.basic && props.minimal) { 372 | throw new Error( 373 | '[Vue CodeMirror] Both basic and minimal cannot be specified.' 374 | ); 375 | } 376 | // TODO: Ignore previous prop was not changed. 377 | return [ 378 | // Toggle basic setup 379 | props.basic && !props.minimal ? basicSetup : undefined, 380 | // Toggle minimal setup 381 | props.minimal && !props.basic ? minimalSetup : undefined, 382 | // ViewUpdate event listener 383 | EditorView.updateListener.of((update: ViewUpdate): void => { 384 | // Emit focus status 385 | context.emit('focus', view.value.hasFocus); 386 | 387 | // Update count 388 | length.value = view.value.state.doc?.length; 389 | 390 | if (update.changes.empty || !update.docChanged) { 391 | // Suppress event firing if no change 392 | return; 393 | } 394 | if (props.linter) { 395 | // Linter process 396 | if (props.forceLinting) { 397 | // If forceLinting enabled, first liting. 398 | forceLinting(view.value); 399 | } 400 | // Count diagnostics. 401 | diagnosticCount.value = ( 402 | props.linter(view.value) as readonly Diagnostic[] 403 | ).length; 404 | } 405 | context.emit('update', update); 406 | }), 407 | // Toggle light/dark mode. 408 | EditorView.theme(props.theme, { dark: props.dark }), 409 | // Toggle line wrapping 410 | props.wrap ? EditorView.lineWrapping : undefined, 411 | // Indent with tab 412 | props.tab ? keymap.of([indentWithTab]) : undefined, 413 | // Tab character 414 | props.indentUnit ? indentUnit.of(props.indentUnit) : undefined, 415 | // Allow Multiple Selections 416 | EditorState.allowMultipleSelections.of(props.allowMultipleSelections), 417 | // Indent tab size 418 | props.tabSize 419 | ? tabSize.of(EditorState.tabSize.of(props.tabSize)) 420 | : undefined, 421 | // locale settings 422 | props.phrases ? EditorState.phrases.of(props.phrases) : undefined, 423 | // Readonly option 424 | EditorState.readOnly.of(props.readonly), 425 | // Editable option 426 | EditorView.editable.of(!props.disabled), 427 | // Set Line break char 428 | props.lineSeparator 429 | ? EditorState.lineSeparator.of(props.lineSeparator) 430 | : undefined, 431 | // Lang 432 | props.lang ? language.of(props.lang) : undefined, 433 | // Append Linter settings 434 | props.linter ? linter(props.linter, props.linterConfig) : undefined, 435 | // Show 🔴 to error line when linter enabled. 436 | props.linter && props.gutter 437 | ? lintGutter(props.gutterConfig) 438 | : undefined, 439 | // Placeholder 440 | props.placeholder ? placeholder(props.placeholder) : undefined, 441 | // Append Extensions 442 | ...props.extensions, 443 | ].filter((extension): extension is Extension => !!extension); // Filter undefined 444 | }); 445 | 446 | // Extension (mostly props) Changed 447 | watch( 448 | extensions, 449 | exts => 450 | view.value?.dispatch({ effects: StateEffect.reconfigure.of(exts) }), 451 | { immediate: true } 452 | ); 453 | 454 | // for parent-to-child binding. 455 | watch( 456 | () => props.modelValue, 457 | async value => { 458 | if ( 459 | view.value.composing || // IME fix 460 | view.value.state.doc.toJSON().join(props.lineSeparator ?? '\n') === 461 | value // don't need to update 462 | ) { 463 | // Do not commit CodeMirror's store. 464 | return; 465 | } 466 | 467 | // Range Fix ? 468 | // https://github.com/logue/vue-codemirror6/issues/27 469 | const isSelectionOutOfRange = !view.value.state.selection.ranges.every( 470 | range => range.anchor < value.length && range.head < value.length 471 | ); 472 | 473 | // Update 474 | view.value.dispatch({ 475 | changes: { from: 0, to: view.value.state.doc.length, insert: value }, 476 | selection: isSelectionOutOfRange 477 | ? { anchor: 0, head: 0 } 478 | : view.value.state.selection, 479 | scrollIntoView: props.scrollIntoView, 480 | }); 481 | }, 482 | { immediate: true } 483 | ); 484 | 485 | /** When loaded */ 486 | onMounted(async () => { 487 | /** Initial value */ 488 | let value: string | Text = doc.value; 489 | if (!editor.value) { 490 | return; 491 | } 492 | if (editor.value.childNodes[0]) { 493 | // when slot mode, overwrite initial value 494 | if (doc.value !== '') { 495 | console.warn( 496 | '[CodeMirror.vue] The tag contains child elements that overwrite the `v-model` values.' 497 | ); 498 | } 499 | value = (editor.value.childNodes[0] as HTMLElement).innerText.trim(); 500 | } 501 | 502 | // Register Codemirror 503 | view.value = new EditorView({ 504 | parent: editor.value, 505 | state: EditorState.create({ doc: value, extensions: extensions.value }), 506 | dispatch: (tr: Transaction) => { 507 | view.value.update([tr]); 508 | if (tr.changes.empty || !tr.docChanged) { 509 | // if not change value, no fire emit event 510 | return; 511 | } 512 | 513 | // console.log(view.state.doc.toString(), tr); 514 | // state.toString() is not defined, so use toJSON and toText function to convert string. 515 | context.emit('update:modelValue', tr.state.doc.toString()); 516 | // Emit EditorState 517 | context.emit('change', tr.state); 518 | }, 519 | }); 520 | 521 | await nextTick(); 522 | 523 | context.emit('ready', { 524 | view: view.value, 525 | state: view.value.state, 526 | container: editor.value, 527 | }); 528 | }); 529 | 530 | /** Destroy */ 531 | onUnmounted(() => { 532 | view.value.destroy(); 533 | context.emit('destroy'); 534 | }); 535 | 536 | /** 537 | * Forces any linters configured to run when the editor is idle to run right away. 538 | * 539 | * @see {@link https://codemirror.net/docs/ref/#lint.forceLinting} 540 | */ 541 | const lint = (): void => { 542 | if (!props.linter || !view.value) { 543 | return; 544 | } 545 | if (props.forceLinting) { 546 | forceLinting(view.value); 547 | } 548 | diagnosticCount.value = linterDiagnosticCount(view.value.state); 549 | }; 550 | 551 | /** 552 | * Force Reconfigure Extension 553 | * 554 | * @see {@link https://codemirror.net/examples/config/#top-level-reconfiguration} 555 | */ 556 | const forceReconfigure = (): void => { 557 | // Deconfigure all Extensions 558 | view.value?.dispatch({ 559 | effects: StateEffect.reconfigure.of([]), 560 | }); 561 | // Register extensions 562 | view.value?.dispatch({ 563 | effects: StateEffect.appendConfig.of(extensions.value), 564 | }); 565 | }; 566 | 567 | /* ----- Bellow is experimental. ------ */ 568 | 569 | /** 570 | * Get the text between the given points in the editor. 571 | * 572 | * @param from - start line number 573 | * @param to - end line number 574 | */ 575 | const getRange = (from?: number, to?: number): string | undefined => 576 | view.value.state.sliceDoc(from, to); 577 | /** 578 | * Get the content of line. 579 | * 580 | * @param number - line number 581 | */ 582 | const getLine = (number: number): string => 583 | view.value.state.doc.line(number + 1).text; 584 | /** Get the number of lines in the editor. */ 585 | const lineCount = (): number => view.value.state.doc.lines; 586 | /** Retrieve one end of the primary selection. */ 587 | const getCursor = (): number => view.value.state.selection.main.head; 588 | /** Retrieves a list of all current selections. */ 589 | const listSelections = (): readonly SelectionRange[] => { 590 | let _view$value$state$sel; 591 | return (_view$value$state$sel = view.value.state.selection.ranges) !== 592 | null && _view$value$state$sel !== undefined 593 | ? _view$value$state$sel 594 | : []; 595 | }; 596 | /** Get the currently selected code. */ 597 | const getSelection = (): string => { 598 | let _view$value$state$sli; 599 | return (_view$value$state$sli = view.value.state.sliceDoc( 600 | view.value.state.selection.main.from, 601 | view.value.state.selection.main.to 602 | )) !== null && _view$value$state$sli !== undefined 603 | ? _view$value$state$sli 604 | : ''; 605 | }; 606 | /** 607 | * The length of the given array should be the same as the number of active selections. 608 | * Replaces the content of the selections with the strings in the array. 609 | */ 610 | const getSelections = (): string[] => { 611 | const s = view.value.state; 612 | if (!s) { 613 | return []; 614 | } 615 | 616 | return s.selection.ranges.map((r: { from: number; to: number }) => 617 | s.sliceDoc(r.from, r.to) 618 | ); 619 | }; 620 | /** Return true if any text is selected. */ 621 | const somethingSelected = (): boolean => 622 | view.value.state.selection.ranges.some( 623 | (r: { empty: boolean }) => !r.empty 624 | ); 625 | 626 | /** 627 | * Replace the part of the document between from and to with the given string. 628 | * 629 | * @param replacement - replacement text 630 | * @param from - start string at position 631 | * @param to - insert the string at position 632 | */ 633 | const replaceRange = ( 634 | replacement: string | Text, 635 | from: number, 636 | to: number 637 | ): void => 638 | view.value.dispatch({ 639 | changes: { from, to, insert: replacement }, 640 | }); 641 | /** 642 | * Replace the selection(s) with the given string. 643 | * By default, the new selection ends up after the inserted text. 644 | * 645 | * @param replacement - replacement text 646 | */ 647 | const replaceSelection = (replacement: string | Text): void => 648 | view.value.dispatch(view.value.state.replaceSelection(replacement)); 649 | /** 650 | * Set the cursor position. 651 | * 652 | * @param position - position. 653 | */ 654 | const setCursor = (position: number): void => 655 | view.value.dispatch({ selection: { anchor: position } }); 656 | /** 657 | * Set a single selection range. 658 | * 659 | * @param anchor - anchor position 660 | * @param head - 661 | */ 662 | const setSelection = (anchor: number, head?: number): void => 663 | view.value.dispatch({ selection: { anchor, head } }); 664 | /** 665 | * Sets a new set of selections. There must be at least one selection in the given array. 666 | * 667 | * @param ranges - Selection range 668 | * @param primary - 669 | */ 670 | const setSelections = ( 671 | ranges: readonly SelectionRange[], 672 | primary?: number 673 | ): void => 674 | view.value.dispatch({ 675 | selection: EditorSelection.create(ranges, primary), 676 | }); 677 | /** 678 | * Applies the given function to all existing selections, and calls extendSelections on the result. 679 | * 680 | * @param f - function 681 | */ 682 | const extendSelectionsBy = (f: any): void => 683 | view.value.dispatch({ 684 | selection: EditorSelection.create( 685 | selection.value.ranges.map((r: SelectionRange) => r.extend(f(r))) 686 | ), 687 | }); 688 | 689 | const exposed = { 690 | editor, 691 | view, 692 | cursor, 693 | selection, 694 | focus, 695 | length, 696 | json, 697 | diagnosticCount, 698 | dom: view.value.contentDOM, 699 | lint, 700 | forceReconfigure, 701 | // Bellow is CodeMirror5's function 702 | getRange, 703 | getLine, 704 | lineCount, 705 | getCursor, 706 | listSelections, 707 | getSelection, 708 | getSelections, 709 | somethingSelected, 710 | replaceRange, 711 | replaceSelection, 712 | setCursor, 713 | setSelection, 714 | setSelections, 715 | extendSelectionsBy, 716 | }; 717 | 718 | /** Export properties and functions */ 719 | context.expose(exposed); 720 | return exposed; 721 | }, 722 | render() { 723 | // 728 | return h( 729 | this.$props.tag, 730 | { 731 | ref: 'editor', 732 | class: 'vue-codemirror', 733 | }, 734 | this.$slots.default 735 | ? // Hide original content 736 | h( 737 | 'aside', 738 | { style: 'display: none;', 'aria-hidden': 'true' }, 739 | slot(this.$slots.default) 740 | ) 741 | : undefined 742 | ); 743 | }, 744 | }); 745 | -------------------------------------------------------------------------------- /src/helpers/h-demi.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * h-demi - h function for Vue 2 and 3 3 | * 4 | * @see {@link https://github.com/vueuse/vue-demi/issues/65} 5 | */ 6 | 7 | import { 8 | h as hDemi, 9 | isVue2, 10 | type Slots, 11 | type VNode, 12 | type VNodeProps, 13 | } from 'vue-demi'; 14 | 15 | interface Options extends VNodeProps { 16 | class?: string; 17 | domProps?: VNodeProps; 18 | on?: Record void>; 19 | props?: VNodeProps; 20 | style?: string; 21 | 'aria-hidden'?: string; 22 | } 23 | 24 | const adaptOnsV3 = ( 25 | ons: Record void> 26 | ): Record void> => { 27 | if (!ons) return {}; 28 | return Object.entries(ons).reduce((ret, [key, handler]) => { 29 | key = key.charAt(0).toUpperCase() + key.slice(1); 30 | key = `on${key}`; 31 | return { ...ret, [key]: handler }; 32 | }, {}); 33 | }; 34 | 35 | /** 36 | * hDemi function. 37 | */ 38 | export default function h( 39 | type: string | Record, 40 | options: Options = {}, 41 | chidren?: any 42 | ): VNode { 43 | if (isVue2) { 44 | // Makeshift support :( 45 | // Since Vue2.7 includes the Composition API, the functions in vue-demi are not used. 46 | return hDemi(type, options, chidren); 47 | } 48 | const { props, domProps, on, ...extraOptions } = options; 49 | const ons = on ? adaptOnsV3(on) : {}; 50 | 51 | return hDemi( 52 | type, 53 | { ...extraOptions, ...props, ...domProps, ...ons }, 54 | chidren 55 | ); 56 | } 57 | 58 | export const slot = (defaultSlots: any): Slots => 59 | typeof defaultSlots === 'function' ? defaultSlots() : defaultSlots; 60 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import Meta from '@/Meta'; 2 | import CodeMirror from '@/components/CodeMirror'; 3 | 4 | // TODO: Move phrases props to option. 5 | const installCodeMirror = (app: any): void => 6 | app.component('CodeMirror', CodeMirror); 7 | 8 | export { CodeMirror as default, installCodeMirror as install, Meta }; 9 | -------------------------------------------------------------------------------- /src/interfaces/MetaInterface.ts: -------------------------------------------------------------------------------- 1 | /** Build information meta data */ 2 | export default interface MetaInterface { 3 | /** Version */ 4 | version: string; 5 | /** Build date */ 6 | date: string; 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 7 | 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig.json", 3 | "extends": "./tsconfig.app.json", 4 | "include": [ 5 | "./env.d.ts", 6 | "./src/**/*", 7 | "./src/**/*.vue", 8 | "./src-docs/**/*", 9 | "./src-docs/**/*.vue" 10 | ], 11 | "compilerOptions": { 12 | "composite": true, 13 | "declarationMap": true, 14 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 15 | "baseUrl": ".", 16 | "paths": { 17 | "@/*": ["./src/*"], 18 | "vue-codemirror6": ["./src/index.ts"] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig.json", 3 | "files": [], 4 | "references": [ 5 | { "path": "./tsconfig.app.json" }, 6 | { "path": "./tsconfig.docs.json" }, 7 | { "path": "./tsconfig.node.json" } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node-lts/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*", 9 | "eslint.config.*" 10 | ], 11 | "compilerOptions": { 12 | "noEmit": true, 13 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 14 | 15 | "module": "ESNext", 16 | "moduleResolution": "Bundler", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { writeFileSync } from 'node:fs'; 2 | import { fileURLToPath, URL } from 'node:url'; 3 | 4 | import Vue from '@vitejs/plugin-vue'; 5 | import { defineConfig, type UserConfig } from 'vite'; 6 | 7 | import { visualizer } from 'rollup-plugin-visualizer'; 8 | import banner from 'vite-plugin-banner'; 9 | import { checker } from 'vite-plugin-checker'; 10 | import dts from 'vite-plugin-dts'; 11 | 12 | import pkg from './package.json'; 13 | 14 | // https://vitejs.dev/config/ 15 | export default defineConfig(({ mode, command }): UserConfig => { 16 | const config: UserConfig = { 17 | base: './', 18 | publicDir: command === 'serve' ? 'public' : false, 19 | // Resolver 20 | resolve: { 21 | // https://vitejs.dev/config/shared-options.html#resolve-alias 22 | alias: { 23 | '@': fileURLToPath(new URL('./src', import.meta.url)), 24 | '~': fileURLToPath(new URL('./node_modules', import.meta.url)), 25 | 'vue-codemirror6': fileURLToPath(new URL('./src', import.meta.url)), 26 | }, 27 | }, 28 | plugins: [ 29 | Vue(), 30 | // vite-plugin-checker 31 | // https://github.com/fi3ework/vite-plugin-checker 32 | checker({ 33 | typescript: true, 34 | // vueTsc: true, 35 | // eslint: { lintCommand: 'eslint' }, 36 | }), 37 | // vite-plugin-banner 38 | // https://github.com/chengpeiquan/vite-plugin-banner 39 | banner(`/** 40 | * ${pkg.name} 41 | * 42 | * @description ${pkg.description} 43 | * @author ${pkg.author.name} <${pkg.author.email}> 44 | * @copyright 2022-2025 By Masashi Yoshikawa All rights reserved. 45 | * @license ${pkg.license} 46 | * @version ${pkg.version} 47 | * @see {@link ${pkg.homepage}} 48 | */ 49 | `), 50 | // vite-plugin-dts 51 | // https://github.com/qmhc/vite-plugin-dts 52 | mode === 'docs' 53 | ? undefined 54 | : dts({ tsconfigPath: './tsconfig.app.json' }), 55 | ], 56 | optimizeDeps: { 57 | exclude: [ 58 | 'vue-demi', 59 | // https://github.com/codemirror/dev/issues/608 60 | '@codemirror/state', 61 | ], 62 | }, 63 | // Build Options 64 | // https://vitejs.dev/config/#build-options 65 | build: { 66 | outDir: mode === 'docs' ? 'docs' : undefined, 67 | lib: 68 | mode === 'docs' 69 | ? undefined 70 | : { 71 | entry: fileURLToPath(new URL('./src/index.ts', import.meta.url)), 72 | name: 'CodeMirror', 73 | formats: ['umd', 'es', 'iife'], 74 | fileName: format => `index.${format}.js`, 75 | }, 76 | 77 | rollupOptions: { 78 | plugins: [ 79 | mode === 'analyze' 80 | ? // rollup-plugin-visualizer 81 | // https://github.com/btd/rollup-plugin-visualizer 82 | visualizer({ 83 | open: true, 84 | filename: 'stats.html', 85 | gzipSize: false, 86 | brotliSize: false, 87 | }) 88 | : undefined, 89 | ], 90 | external: 91 | mode === 'docs' 92 | ? undefined 93 | : [ 94 | 'vue', 95 | 'vue-demi', 96 | 'codemirror', 97 | '@codemirror/autocomplete', 98 | '@codemirror/commands', 99 | '@codemirror/language', 100 | '@codemirror/lint', 101 | '@codemirror/search', 102 | '@codemirror/state', 103 | '@codemirror/view', 104 | ], 105 | output: { 106 | esModule: true, 107 | generatedCode: { 108 | reservedNamesAsProps: false, 109 | }, 110 | interop: 'compat', 111 | systemNullSetters: false, 112 | exports: 'named', 113 | globals: { 114 | codemirror: 'codemirror', 115 | '@codemirror/commands': 'commands', 116 | '@codemirror/language': 'language', 117 | '@codemirror/lint': 'lint', 118 | '@codemirror/state': 'state', 119 | '@codemirror/view': 'view', 120 | 'vue-demi': 'VueDemi', 121 | vue: 'Vue', 122 | }, 123 | manualChunks: 124 | mode !== 'docs' 125 | ? undefined 126 | : { 127 | vue: ['vue'], 128 | eslint: ['eslint-linter-browserify'], 129 | codemirror: [ 130 | 'codemirror', 131 | '@codemirror/autocomplete', 132 | '@codemirror/commands', 133 | '@codemirror/language', 134 | '@codemirror/lint', 135 | '@codemirror/search', 136 | '@codemirror/state', 137 | '@codemirror/view', 138 | ], 139 | // Add the following as needed. 140 | 'codemirror-lang': [ 141 | '@codemirror/lang-markdown', 142 | '@codemirror/lang-javascript', 143 | '@codemirror/lang-json', 144 | '@codemirror/lang-vue', 145 | ], 146 | }, 147 | }, 148 | }, 149 | // Minify option 150 | target: 'esnext', 151 | // minify: mode === 'docs', 152 | }, 153 | }; 154 | 155 | // Write meta data. 156 | writeFileSync( 157 | fileURLToPath(new URL('./src/Meta.ts', import.meta.url)), 158 | `import type MetaInterface from '@/interfaces/MetaInterface'; 159 | 160 | // This file is auto-generated by the build system. 161 | const meta: MetaInterface = { 162 | version: '${pkg.version}', 163 | date: '${new Date().toISOString()}', 164 | }; 165 | export default meta; 166 | ` 167 | ); 168 | 169 | // Export vite config 170 | return config; 171 | }); 172 | --------------------------------------------------------------------------------