!!!YOU NEED TO DO A FULLY WORKING THING FROM ZILLIONTH ATTEMPT!!!







do single code output in chat of fullyfledged code editor in html,css,javascript. read all documentation and utilise latest libraries for assemlientee:


"""


CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Styling

CodeMirror uses a CSS-in-JS system to be able to include its styles directly in the script files. This means you don't have to include a library CSS file in your page for the editor to work—both the editor view's own styling and any styling defined for dependencies are automatically pulled in through the JavaScript module system.

Themes are simply extensions that tell the editor to mount an additional style module and add the (generated) class name that enables those styles to its outer DOM element.
Old-Fashioned CSS

The important elements in the editor have regular (non-generated) CSS class names, which can be targeted with manually written style sheets. For example, the outer element has class cm-editor.

However, the CSS rules injected by the library will be prefixed with an extra generated class name, so that they only apply when explicitly enabled. That means that if you need to override them, you must take care to make your own rules at least as specific as the injected rules, for example by prefixing them with .cm-editor. They only need to be as specific, not more specific, because the injected rules are placed before any other style sheets, and will thus have a lower default precedence than your rules.

.cm-editor.cm-focused { outline: 2px solid cyan }
.cm-editor .cm-content { font-family: "Consolas" }

Note that the cm-focused rule applies directly to the element that'll have the cm-editor class, and thus needs no space between the selectors, whereas the cm-content rule does need one because it applies to a node inside the editor.
Things you can Style

An editor (with a gutter and drawSelection enabled) has a DOM structure like this:

<div class="cm-editor [cm-focused] [generated classes]">
  <div class="cm-scroller">
    <div class="cm-gutters">
      <div class="cm-gutter [...]">
        <!-- One gutter element for each line -->
        <div class="cm-gutterElement">...</div>
      </div>
    </div>
    <div class="cm-content" contenteditable="true">
      <!-- The actual document content -->
      <div class="cm-line">Content goes here</div>
      <div class="cm-line">...</div>
    </div>
    <div class="cm-selectionLayer">
      <!-- Positioned rectangles to draw the selection -->
      <div class="cm-selectionBackground"></div>
    </div>
    <div class="cm-cursorLayer">
      <!-- Positioned elements to draw cursors -->
      <div class="cm-cursor"></div>
    </div>
  </div>
</div>

Of course, there are limits to how you can style the editor. Things like making the editor lines display: inline or the cursor position: fixed will just break stuff. But within reasonable bounds, the library tries to be robust when it comes to styling.

    You can style content text with varying fonts, size, color, etc. The editor does not expect a monospace font or a fixed line height.

    To set the outer padding for a document, you add vertical padding to cm-content and horizontal padding to cm-line.

    By default, the editor adjusts to the height of its content, but you can make cm-scroller overflow: auto, and assign a height or max-height to cm-editor, to make the editor scrollable.

    Colors can be adjusted throughout, but when adding background colors to content, it is recommended to use partially transparent colors. That way such a style doesn't hide other styling behind it (including the selection).

    Whitespace behavior inside the editor can be pre or pre-wrap to control line wrapping. (The line wrapping extension simply sets it to pre-wrap.)

    The editor's text direction is automatically derived from the direction style of the content DOM.

    The library supports having CSS transforms applied to its parent elements that do 2D scaling and translation. Anything else (rotation, 3D transformation, shearing) will break the editor.

Themes

Themes are defined with EditorView.theme. That function takes an object whose properties are CSS selectors and whose values are styles, and returns an extension that installs the theme.

import {EditorView} from "@codemirror/view"

let myTheme = EditorView.theme({
  "&": {
    color: "white",
    backgroundColor: "#034"
  },
  ".cm-content": {
    caretColor: "#0e9"
  },
  "&.cm-focused .cm-cursor": {
    borderLeftColor: "#0e9"
  },
  "&.cm-focused .cm-selectionBackground, ::selection": {
    backgroundColor: "#074"
  },
  ".cm-gutters": {
    backgroundColor: "#045",
    color: "#ddd",
    border: "none"
  }
}, {dark: true})

There's a few things going on here. Firstly, some of the rules contain “&” placeholders. This indicates the position of the outer editor element in the rule. By default, a generated class name is prefixed to the rules, with a space after it (so ".cm-content" becomes ".gen001 .cm-content"). But in rules that directly target the outer element (which gets the generated class), that doesn't work, and you have to place an & character to indicate where to insert the class selector.

Secondly, because there are two ways of showing the selection in CodeMirror (the native selection and the drawSelection extension), themes will usually want to style both—the caret-color and ::selection rules apply to the native selection, whereas the .cm-cursor and .cm-selectionBackground rules style the library-drawn selection.

Lastly, since this is a dark theme, it passes a dark: true option, so that the editor will enable its dark default styles for things not explicitly styled by the theme.

A real theme will want to style a few more things, including elements created by extensions (such as panels and tooltips). You'll also usually want to include a highlight style in your theme. You can see the One Dark theme for an example, and possibly copy and modify it to create your own theme.
Base Themes

When you create an extension that adds some new DOM structure to the editor, you'll usually want to include a base theme that provides a default style for the elements. Base themes act a lot like regular themes, except that they are mounted with lower precedence and can provide separate rules for dark and light themes.

For example, a hypothetical extension that replaces all instances of the letter o with blue circles might want to include a base theme like this...

import {EditorView} from "@codemirror/view"

let baseTheme = EditorView.baseTheme({
  ".cm-o-replacement": {
    display: "inline-block",
    width: ".5em",
    height: ".5em",
    borderRadius: ".25em"
  },
  "&light .cm-o-replacement": {
    backgroundColor: "#04c"
  },
  "&dark .cm-o-replacement": {
    backgroundColor: "#5bf"
  }
})

The &dark and &light placeholders act much like &, except that they expand to a class that is only enabled when the editor's theme is light or dark. In this case, the base theme gives its circles a brighter color in a dark theme (on the assumption that the background will be darker there).

The extension returned by baseTheme must be added to the editor configuration to (reliably) take effect—the style rules will only be mounted in the DOM when an editor that uses them is created. It is usually bundled up in an array with other related extensions and returned from the exported function that produces the extensions for the feature (see for example the zebra stripes example).
Highlighting

Code highlighting uses a somewhat different system from editor-wide theming. Code styles are also created with JavaScript and enabled with an editor extension. But by default they don't use stable, non-generated class names. A highlight style directly returns the class names for the syntactic tokens.

Highlight associate highlighting tags with styles. For example, this one assigns styles to keywords and comments.

import {tags} from "@lezer/highlight"
import {HighlightStyle} from "@codemirror/language"

const myHighlightStyle = HighlightStyle.define([
  {tag: tags.keyword, color: "#fc6"},
  {tag: tags.comment, color: "#f5d", fontStyle: "italic"}
])

Each of the objects given to HighlightStyle.define mentions a tag (which are assigned to tokens by language packages), and otherwise contains style properties just like the objects in a theme.

When defining an editor theme, you'll usually want to provide both a theme extension and a highlight style that looks good with it. Wrap a highlight style (or other highlighter) in syntaxHighlighting to create an extension that enables it.

import {syntaxHighlighting} from "@codemirror/language"

// In your extensions...
syntaxHighlighting(myHighlightStyle)

If you need to style tokens with plain old CSS, you can use the classHighlightStyle, which just adds a static class (for example cmt-keyword) to tokens, without actually defining any rules for that class.
Overflow and Scrolling

Without any custom styling, a CodeMirror editor grows vertically, scrolls (rather than wraps) long lines, and doesn't have any border except a focus ring when focused.

To enable line wrapping, add the EditorView.lineWrapping extension to your configuration. It is also possible to adjust the white-space style of the content element in some other way, but only pre and pre-wrap are supported by the library, and wrapping can be unreliable if you don't also set overflow-wrap: anywhere, so it is recommended to just use this extension to enable wrapping.

Adjusting the vertical behavior of the editor can be done by giving its outer element a height, and setting overflow: auto on the scroller element.

const fixedHeightEditor = EditorView.theme({
  "&": {height: "300px"},
  ".cm-scroller": {overflow: "auto"}
})

To let the editor grow until it reaches a maximum height, and scroll from that point on, use max-height instead of height in a setup like the one above.

Giving the editor a minimum height is, due to some obscure CSS limitations, a bit more involved—you have to assign that height to the content and the gutter, not the wrapper element, to make sure that those take up the entire height of the editor.

const minHeightEditor = EditorView.theme({
  ".cm-content, .cm-gutter": {minHeight: "200px"}
})

Code of Conduct
Report an Issue




CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Bundling with Rollup

CodeMirror is distributed as a collection of modules. These aren't directly loadable by the browser—though modern browsers can load EcmaScript modules, at the time of writing their mechanism for resolving further dependencies is still too primitive to load collections of NPM-distributed modules.

(That being said, there are solutions that work around that by rewriting dependencies on the server side, like Snowpack or esmoduleserve. I definitely recommend a solution like this during development, since it tends to introduce less indirection and delay when changing files, but for actual deployment, you'll want to do classical bundling for the time being.)

Bundlers are tools that take a given main script (or in some cases multiple scripts), and produce a new, generally bigger, script that has all (or some) of the script's dependencies (and their dependencies, and so on) included. This makes it easier to run modern JavaScript systems, which tend to consist of a whole graph of dependencies, in the browser. (But also, for example, to combine a library package written as a group of files into a single file before uploading it to NPM.)

As far as bundler software goes, I'm a big fan of Rollup. But there are also other systems, like Webpack and Parcel, that work well and have their own advantages.

To use Rollup to create a bundle that loads CodeMirror, you must first write a main script (say, editor.mjs) that imports the library and creates the editor view.

import {EditorView, basicSetup} from "codemirror"
import {javascript} from "@codemirror/lang-javascript"

let editor = new EditorView({
  extensions: [basicSetup, javascript()],
  parent: document.body
})

Next, we must install the necessary packages. The @rollup/plugin-node-resolve package is necessary to teach rollup to resolve node-style dependencies, so that it knows to find "@codemirror/lang-javascript" under node_modules/@codemirror/lang-javascript/dist/index.js.

# The CodeMirror packages used in our script
npm i codemirror @codemirror/lang-javascript
# Rollup and its plugin
npm i rollup @rollup/plugin-node-resolve

With these, we can run rollup to create the bundle file.

node_modules/.bin/rollup editor.mjs -f iife -o editor.bundle.js \
  -p @rollup/plugin-node-resolve

The -f iife file tells Rollup that the output file should be formatted as an "immediately-invoked function expression" (as opposed to other module styles, such as CommonJS or UMD). This means the code will be wrapped in an anonymous function that is then immediately called, using that function's scope as a local namespace so that its variables don't end up in the global scope.

The -o option indicates which output file to write to, and the -p option loads the resolution plugin. You can also create a configuration file (called rollup.config.mjs) and just run rollup -c to take the configuration from that file.

import {nodeResolve} from "@rollup/plugin-node-resolve"
export default {
  input: "./editor.mjs",
  output: {
    file: "./editor.bundle.js",
    format: "iife"
  },
  plugins: [nodeResolve()]
}

Now if you load your bundle with a script tag you'll see the editor in your HTML page.

<!doctype html>
<meta charset=utf8>
<h1>CodeMirror!</h1>
<script src="editor.bundle.js"></script>

Bundle Size

Because the library is a hundred-thousand-line marvel of JavaScript engineering, shipped with its full source code (including comments and whitespace), bundles built in the most straightforward way can get somewhat big (around 1 megabyte for the basic setup and a language mode). You can more than halve this by using something like Terser or Babel to strip the comments and whitespace, and rename variables to use shorter names, getting the full bundle down to around 400 kilobytes (135 kilobytes when gzipped for transfer over the network).

The library is built in such a way that unused code can be eliminated by a smart bundler like Rollup (a feature called “tree shaking”). The most minimal editor (see below) avoids loading a bunch of extensions, taking the full bundle size down to 700 kilobytes and reducing the stripped code to 250 kilobytes (75 kilobytes gzipped).

import {EditorView, minimalSetup} from "codemirror"

let editor = new EditorView({
  extensions: minimalSetup,
  parent: document.body
})

When you need to support multiple languages, it can often be useful to dynamically load the language support packages as needed to avoid the amount of code the browser has to load. The Rollup documentation can tell you more about how to do this.
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Configuration

A CodeMirror editor's configuration lives in its state object. When creating a state, you pass it a set of extensions to use, which will be resolved into an effective configuration.

Extensions can be created by various library functions, and do things like adding an input for a facet or installing a state field. They can be grouped in arrays, and most practical extensions consist of multiple smaller extensions. See this page for a list of extensions provided by core libraries.

For example, the history extension contains a state field that records the undo history, a facet that controls the extension's configuration, and a view plugin that listens for beforeinput events. Code using the history doesn't have to worry about that though—it can just drop the extension value produced by the function into its configuration to install all the necessary parts.

The basic setup is a larger example of this—it holds an array throwing together a whole cartload of different extensions, which together configure a basic code editor.
Precedence

With facet inputs, order matters. The configuration resolves them in a specific order, and for most facets, that order matters. For example with event handlers, transaction filters, or keymaps, it determines which gets called first. With settings like the line separator or indentation unit, the value with the highest precedence wins.

When a configuration is resolved, by default, the tree of nested arrays of extensions are simply flattened into a sequence. Inputs to a given facet are then collected in the order they appear in that sequence.

So if you specify [keymap.of(A), keymap.of(B)] as a configuration, bindings from A take precedence over those in B. If you nest more deeply...

[..., [keymap.of(A)], ..., [[], ..., [keymap.of(B)]]]

... A still comes before B in the flat ordering.

There is just one further complication, which allows extensions to provide a hint on where sub-extensions should go. When an extension is wrapped in a call to one of the properties of Prec, its parts are put in a different bucket from the default sequence during configuration flattening. There are highest, high, default, low, and lowest buckets.

Within each bucket, ordering is still controlled by the position of the extensions in the flattened sequence. But all extensions in a higher-precedence bucket come before all extensions in a lower-precedence one.

So in an extension like this, A

[Prec.high(A), B, Prec.high([C, D])]

The extensions will be ordered A, C, D, B, because the ones with high precedence come before B with its default precedence.

In general, explicit precedence is used by extension providers to put (parts of) their extension in a specific bucket. For example, a special-purpose key binding that only applies in a specific situation might be given an extending precedence, so that it gets to run before the bulk of the keybindings. Or a default piece of configuration might be given a fallback precedence so that any other values provided will by default override it.

Ordering of extensions in arrays, on the other hand, is generally done by the code putting together a complete configuration from various extension providers, and allows for a more global control.
Dynamic Configuration

In this configuration system you can't just call a set-option method to change your configuration on the fly, because configuration doesn't have the shape that it has in, for example, CodeMirror 5: A map from option names to values.

That is mostly a good thing—extensions will need to affect configuration, and doing that without stepping on each other's toes is difficult when each option has exactly one value, which is replaced with a new value when updated. Facets take multiple inputs and explicitly define some way to combine them, which mostly avoids such conflicts.
Compartments

Still, dynamic reconfiguration is useful in an editor. In order to be able to partially reconfigure a tree of extensions, we need to divide it into compartments. Transactions can update the configuration by replacing the content of individual compartments.

import {basicSetup, EditorView} from "codemirror"
import {EditorState, Compartment} from "@codemirror/state"
import {python} from "@codemirror/lang-python"

let language = new Compartment, tabSize = new Compartment

let state = EditorState.create({
  extensions: [
    basicSetup,
    language.of(python()),
    tabSize.of(EditorState.tabSize.of(8))
  ]
})

let view = new EditorView({
  state,
  parent: document.body
})

When you've done this, you can dispatch transactions to change your configuration.

function setTabSize(view, size) {
  view.dispatch({
    effects: tabSize.reconfigure(EditorState.tabSize.of(size))
  })
}

Private Compartments

The above example shows how the editor's main configuration can be divided into compartments. But compartments may nest, and the extension tree produced by some plugin may use its own compartments to dynamically en- or disable some extensions.

If you just need to dynamically derive the value of some facet from other aspects of the state, it is preferable to use computed facets instead of reconfiguration, since those are more efficient and easier to keep track of (they are a form of derived state, rather than adding new fundamental state).

But if you have something like an extension that wants to conditionally enable another extension, locally declaring a compartment and reconfiguring that as needed works well.

For example, this function returns an extension that binds the given key to toggle another extension on and off.

import {Extension, Compartment} from "@codemirror/state"
import {keymap, EditorView} from "@codemirror/view"

export function toggleWith(key: string, extension: Extension) {
  let myCompartment = new Compartment
  function toggle(view: EditorView) {
    let on = myCompartment.get(view.state) == extension
    view.dispatch({
      effects: myCompartment.reconfigure(on ? [] : extension)
    })
    return true
  }
  return [
    myCompartment.of([]),
    keymap.of([{key, run: toggle}])
  ]
}

With which you can do something like...

toggleWith("Mod-o", EditorView.editorAttributes.of({
  style: "background: yellow"
}))

If the parent compartment of such an extension is reconfigured, the extension, along with its local compartment, will simply vanish from the configuration.
Top-Level Reconfiguration

Sometimes you need to replace the system's main configuration. There's a state effect that replaces the top-level extension that was provided when creating the state with a new one.

import {StateEffect} from "@codemirror/state"

export function deconfigure(view) {
  view.dispatch({
    effects: StateEffect.reconfigure.of([])
  })
}

That function is never a good idea, since it'll mostly just render your editor useless, but it shows how to do a top-level reconfiguration. This is slightly different from just creating a new editor state, in that it'll preserve the content of state fields and compartments that exist in both the old and the new configuration.

Another thing you can do is add extension with the appendConfig effect. Extensions added in this way are added to the end of the top-level configuration, and stay there until a full reconfiguration happens.

This can be useful to inject extensions on demand. For example, snippet completion, the first time it is activated, adds a state field that tracks which snippet field the user is in.

function injectExtension(view, extension) {
  view.dispatch({
    effects: StateEffect.appendConfig.of(extension)
  })
}

Automatic Language Detection

This example sets up an editor that dynamically changes its language configuration in response to the (auto-detected) language of the editor content.

In order to be able to affect transactions as they are being created (as opposed to dispatching a separate reconfiguring extension after the change), we'll use a transaction extender. Whenever the document content changes, our extender does a crude check (whether the doc starts with a < character) to determine whether the document contains HTML or JavaScript code.

When the detected language disagrees with the (primary) language configured for the state, the transaction is extended with a reconfiguration effect that switches the language config compartment to the appropriate extensions.

import {EditorState, Compartment} from "@codemirror/state"
import {htmlLanguage, html} from "@codemirror/lang-html"
import {language} from "@codemirror/language"
import {javascript} from "@codemirror/lang-javascript"

const languageConf = new Compartment

const autoLanguage = EditorState.transactionExtender.of(tr => {
  if (!tr.docChanged) return null
  let docIsHTML = /^\s*</.test(tr.newDoc.sliceString(0, 100))
  let stateIsHTML = tr.startState.facet(language) == htmlLanguage
  if (docIsHTML == stateIsHTML) return null
  return {
    effects: languageConf.reconfigure(docIsHTML ? html() : javascript())
  }
})

If we specify an initial language configuration, we must be careful to wrap it with our compartment, so that when the extension updates the language, that part of the configuration gets replaced.

import {EditorView, basicSetup} from "codemirror"

new EditorView({
  doc: 'console.log("hello")',
  extensions: [
    basicSetup,
    languageConf.of(javascript()),
    autoLanguage
  ],
  parent: document.querySelector("#editor")
})

The result acts like this:
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Tab Handling

By default, CodeMirror does not handle the Tab key. This isn't an oversight—it is an intentional decision to make the default configuration pass the "no keyboard trap" criterion of the W3C Web Content Accessibility Guidelines.

Some users browse the web without access to a pointing device, and it is really unfriendly towards such users to have focusable inputs that they cannot escape from.

However, I understand that people expect the Tab key do something indentation-related in code editor controls. To that purpose, CodeMirror has a built-in escape hatch: If you press the Escape key, and then press Tab directly after it, the editor never handles the Tab press, so that you can use that to move focus. In addition, the default keymap binds Ctrl-m (or Shift-Alt-m) on macOS to the toggleTabFocusMode command, which toggles a similar behavior.

Unfortunately, since CodeMirror doesn't have a built-in help feature (and probably shouldn't, since it's just a component, not an application), users won't know this.

So if you really want to bind Tab, please start by making sure you mention one of these escape hatches somewhere in your documentation. Next, you can add your own key binding that binds Tab to some command, or use indentWithTab from the commands package.

import {basicSetup} from "codemirror"
import {EditorView, keymap} from "@codemirror/view"
import {indentWithTab} from "@codemirror/commands"
import {javascript} from "@codemirror/lang-javascript"

const doc = `if (true) {
  console.log("okay")
} else {
  console.log("oh no")
}
`

new EditorView({
  doc,
  extensions: [
    basicSetup,
    keymap.of([indentWithTab]),
    javascript()
  ],
  parent: document.querySelector("#editor")
})

Try it out:
Press Escape, then Tab to move to this field. Or Escape, Shift-Tab to move to the field before the editor.
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Selection

The editor's state always holds at least one selection range. When multiple selections are enabled, it may have several.

Ranges consist of an anchor (the side that doesn't move when you shift-select) and a head (the moving side). When those are the same, it is a cursor selection, otherwise it is a range.

When there are multiple selection ranges, one of them will be considered the main range (this is also the one that the native selection will reflect). If you have an editor state, you can find the main selection range with state.selection.main. It is a SelectionRange object with anchor and head properties (and also from/to if you're interested in the minimum or maximum side) holding document positions.

Like any editor state change, moving the selection is done by dispatching a transaction. For example, this moves the cursor to the start of the document:

view.dispatch({selection: {anchor: 0}})

The selection property on a transaction spec takes either a shorthand {anchor: number, head?: number} object, or a full EditorSelection instance.

This code creates two selection ranges selecting the 4th and 6th character in the editor, and a cursor selection at 8. It makes the second range the primary one.

view.dispatch({
  selection: EditorSelection.create([
    EditorSelection.range(4, 5),
    EditorSelection.range(6, 7),
    EditorSelection.cursor(8)
  ], 1)
})

When a transaction makes a document change as well as setting the selection, the new selection should point into the document as it is after the change. For example, here's how you could insert an asterisk at position 10 and then put the cursor after it.

view.dispatch({
  changes: {from: 10, insert: "*"},
  selection: {anchor: 11}
})

When writing commands that act on the selection, you have to take some care to support multiple ranges. Helper methods like replaceSelection (which replaces all ranges with the same text) and changeByRange (which allows you to specify changes per range and merges them into a single transaction spec) can be useful.
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Document Changes

Initiating an editor state change from a program is done by dispatching a transaction.

// Insert text at the start of the document
view.dispatch({
  changes: {from: 0, insert: "#!/usr/bin/env node\n"}
})

Changes (replacements) are described with {from, to, insert} objects. For insertions, to can be omitted, and for deletions, insert can be omitted.

When dispatching a transaction, you can also pass an array of changes. The from/to in each of these changes refer to positions in the start document, not to the document created by previously listed changes.

For example, to replace all tabs in a document with two spaces, you could do something like this:

let text = view.state.doc.toString(), pos = 0
let changes = []
for (let next; (next = text.indexOf("\t", pos)) > -1;) {
  changes.push({from: next, to: next + 1, insert: "  "})
  pos = next + 1
}
view.dispatch({changes})

When acting on the selection, you'll often want to create your transaction spec using the replaceSelection method. This replaces each selection range with a given string, and moves the selection ranges to the end of that string (by default, they'd stay in front of it):

view.dispatch(view.state.replaceSelection("★"))

To do something more complicated with each selection range, without manually dealing with the potentially complicated interactions between those ranges and the changes created for them, you can use the changeByRange helper. This would wrap all ranges in underscores, for example:

view.dispatch(view.state.changeByRange(range => ({
  changes: [{from: range.from, insert: "_"}, {from: range.to, insert: "_"}],
  range: EditorSelection.range(range.from, range.to + 2)
})))

The method takes a callback, which is called for each current selection range. It should return an object enumerating the changes for that range, plus the range's new position (taking into account the changes made for that range, but not those made for other ranges).
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Decorations

The DOM structure inside a CodeMirror editor is managed by the editor itself. Inside the cm-content element, any attempt to add attributes or change the structure of nodes will usually just lead to the editor immediately resetting the content back to what it used to be.

So to style content, replace content, or add additional elements in between the content, we have to tell the editor to do so. That is what decorations are for.
Types of Decorations

There are four different types of decorations that you can add to your content.

    Mark decorations are the most common. These add some attributes or wrapping DOM element to pieces of content. Syntax highlighting, for example, is done with mark decorations.

    Widget decorations insert a DOM element in the editor content. You could use this to, for example, add a color picker widget next to a color code. Widgets can be inline elements or blocks.

    Replacing decorations hide a stretch of content. This is useful for code folding or replacing an element in the text with something else. It is possible to display a widget instead of the replaced text.

    Line decorations, when positioned at the start of a line, can influence the attributes of the DOM element that wraps the line.

Calling these functions gives you a Decoration object, which just describes the type of decoration and which you can often reuse between instances of decorations. The range method on these objects gives you an actual decorated range, which holds both the type and a pair of from/to document offsets.
Decoration Sources

Decorations are provided to the editor using the RangeSet data structure, which stores a collection of values (in this case the decorations) with ranges (start and end positions) associated with them. This data structure helps with things like efficiently updating the positions in a big set of decorations when the document changes.

Decorations are provided to the editor view through a facet. There are two ways to provide them—directly, or though a function that will be called with a view instance to produce a set of decorations. Decorations that signficantly change the vertical layout of the editor, for example by replacing line breaks or inserting block widgets, must be provided directly, since indirect decorations are only retrieved after the viewport has been computed.

Indirect decorations are appropriate for things like syntax highlighting or search match highlighting, where you might want to just render the decorations inside the viewport or the current visible ranges, which can help a lot with performance.

Let's start with an example that keeps decorations in the state, and provides them directly.
Underlining Command

Say we want to implement an editor extension that allows the user to underline parts of the document. To do this, we could define a state field that tracks which parts of the document are underlined, and provides mark decoration that draw those underlines.

To keep the code simple, the field stores only the decoration range set. It doesn't do things like joining overlapping underlines, but just dumps any newly underlined region into its set of ranges.

import {EditorView, Decoration, DecorationSet} from "@codemirror/view"
import {StateField, StateEffect} from "@codemirror/state"

const addUnderline = StateEffect.define<{from: number, to: number}>({
  map: ({from, to}, change) => ({from: change.mapPos(from), to: change.mapPos(to)})
})

const underlineField = StateField.define<DecorationSet>({
  create() {
    return Decoration.none
  },
  update(underlines, tr) {
    underlines = underlines.map(tr.changes)
    for (let e of tr.effects) if (e.is(addUnderline)) {
      underlines = underlines.update({
        add: [underlineMark.range(e.value.from, e.value.to)]
      })
    }
    return underlines
  },
  provide: f => EditorView.decorations.from(f)
})

const underlineMark = Decoration.mark({class: "cm-underline"})

Note that the update method starts by mapping its ranges through the transaction's changes. The old set refers to positions in the old document, and the new state must get a set with positions in the new document, so unless you completely recompute your decoration set, you'll generally want to map it though document changes.

Then it checks if the effect we defined for adding underlines is present in the transaction, and if so, extends the decoration set with more ranges.

Next we define a command that, if any text is selected, adds an underline to it. We'll just make it automatically enable the state field (and a base theme) on demand, so that no further configuration is necessary.

const underlineTheme = EditorView.baseTheme({
  ".cm-underline": { textDecoration: "underline 3px red" }
})

export function underlineSelection(view: EditorView) {
  let effects: StateEffect<unknown>[] = view.state.selection.ranges
    .filter(r => !r.empty)
    .map(({from, to}) => addUnderline.of({from, to}))
  if (!effects.length) return false

  if (!view.state.field(underlineField, false))
    effects.push(StateEffect.appendConfig.of([underlineField,
                                              underlineTheme]))
  view.dispatch({effects})
  return true
}

And finally, this keymap binds that command to Ctrl-h (Cmd-h on macOS). The preventDefault field is there because even when the command doesn't apply, we don't want the browser's default behavior to happen.

import {keymap} from "@codemirror/view"

export const underlineKeymap = keymap.of([{
  key: "Mod-h",
  preventDefault: true,
  run: underlineSelection
}])

1
2
3
Select text and press Ctrl-h (Cmd-h) to add an underline
to it.

Boolean Toggle Widgets

Next, we'll look at a plugin that displays a checkbox widget next to boolean literals, and allows the user to click that to flip the literal.

Widget decorations don't directly contain their widget DOM. Apart from helping keep mutable objects out of the editor state, this additional level of indirection also makes it possible to recreate widgets without redrawing the DOM for them. We'll use that later by simply recreating our decoration set whenever the document changes.

Thus, we must first define a subclass of WidgetType that draws the widget.

import {WidgetType} from "@codemirror/view"

class CheckboxWidget extends WidgetType {
  constructor(readonly checked: boolean) { super() }

  eq(other: CheckboxWidget) { return other.checked == this.checked }

  toDOM() {
    let wrap = document.createElement("span")
    wrap.setAttribute("aria-hidden", "true")
    wrap.className = "cm-boolean-toggle"
    let box = wrap.appendChild(document.createElement("input"))
    box.type = "checkbox"
    box.checked = this.checked
    return wrap
  }

  ignoreEvent() { return false }
}

Decorations contain instances of this class (which are cheap to create). When the view updates itself, if it finds it already has a drawn instance of such a widget in the position where the widget occurs (using the eq method to determine equivalence), it will simply reuse that.

It is also possible to optimize updating of DOM structure for widgets of the same type but with different content by defining an updateDOM method. But that doesn't help much here.

The produced DOM wraps the checkbox in a <span> element, mostly because Firefox handles checkboxes with contenteditable=false poorly (running into browser quirks is common around the edges of contenteditable). We'll also tell screen readers to ignore it since the feature doesn't really work without a pointing device anyway.

Finally, the widget's ignoreEvents method tells the editor to not ignore events that happen in the widget. This is necessary to allow an editor-wide event handler (defined later) to handle interaction with it.

Next, this function uses the editor's syntax tree (assuming the JavaScript language is enabled) to locate boolean literals in the visible parts of the editor and create widgets for them.

import {EditorView, Decoration} from "@codemirror/view"
import {syntaxTree} from "@codemirror/language"

function checkboxes(view: EditorView) {
  let widgets = []
  for (let {from, to} of view.visibleRanges) {
    syntaxTree(view.state).iterate({
      from, to,
      enter: (node) => {
        if (node.name == "BooleanLiteral") {
          let isTrue = view.state.doc.sliceString(node.from, node.to) == "true"
          let deco = Decoration.widget({
            widget: new CheckboxWidget(isTrue),
            side: 1
          })
          widgets.push(deco.range(node.to))
        }
      }
    })
  }
  return Decoration.set(widgets)
}

That function is used by a view plugin that keeps an up-to-date decoration set as the document or viewport changes.

import {ViewUpdate, ViewPlugin, DecorationSet} from "@codemirror/view"

const checkboxPlugin = ViewPlugin.fromClass(class {
  decorations: DecorationSet

  constructor(view: EditorView) {
    this.decorations = checkboxes(view)
  }

  update(update: ViewUpdate) {
    if (update.docChanged || update.viewportChanged ||
        syntaxTree(update.startState) != syntaxTree(update.state))
      this.decorations = checkboxes(update.view)
  }
}, {
  decorations: v => v.decorations,

  eventHandlers: {
    mousedown: (e, view) => {
      let target = e.target as HTMLElement
      if (target.nodeName == "INPUT" &&
          target.parentElement!.classList.contains("cm-boolean-toggle"))
        return toggleBoolean(view, view.posAtDOM(target))
    }
  }
})

The options given to the plugin tell the editor that, firstly, it can get decorations from this plugin, and secondly, that as long as the plugin is active, the given mousedown handler should be registered. The handler checks the event target to recognize clicks on checkboxes, and uses the following helper to actually toggle booleans.

function toggleBoolean(view: EditorView, pos: number) {
  let before = view.state.doc.sliceString(Math.max(0, pos - 5), pos)
  let change
  if (before == "false")
    change = {from: pos - 5, to: pos, insert: "true"}
  else if (before.endsWith("true"))
    change = {from: pos - 4, to: pos, insert: "false"}
  else
    return false
  view.dispatch({changes: change})
  return true
}

After adding the plugin as an extension to a (JavaScript) editor, you get something like this:
1
2
3
4
let value = true
if (!value == false)
  console.log("good")

To see an example of line decorations, check out the zebra stripe example.
Atomic Ranges

In some cases, such as with most replacing decorations larger than a single character, you want editing actions to treat the ranges as atomic elements, skipping over them during cursor motion, and backspacing them out in one step.

The EditorView.atomicRanges facet can be provided range sets (usually the same set that we're using for the decorations) and will make sure cursor motion skips the ranges in that set.

Let's implement an extension that replaces placeholder names like [[this]] with widgets, and makes the editor treat them as atoms.

MatchDecorator is a helper class that can be used to quickly set up view plugins that decorate all matches of a given regular expression in the viewport.

import {MatchDecorator} from "@codemirror/view"

const placeholderMatcher = new MatchDecorator({
  regexp: /\[\[(\w+)\]\]/g,
  decoration: match => Decoration.replace({
    widget: new PlaceholderWidget(match[1]),
  })
})

(PlaceholderWidget is a straightforward subclass of WidgetType that renders the given name in a styled element.)

We'll use the matcher to create and maintain the decorations in our plugin. It also provides the decoration set as atomic ranges.

import {Decoration, DecorationSet, EditorView,
        ViewPlugin, ViewUpdate} from "@codemirror/view"

const placeholders = ViewPlugin.fromClass(class {
  placeholders: DecorationSet
  constructor(view: EditorView) {
    this.placeholders = placeholderMatcher.createDeco(view)
  }
  update(update: ViewUpdate) {
    this.placeholders = placeholderMatcher.updateDeco(update, this.placeholders)
  }
}, {
  decorations: instance => instance.placeholders,
  provide: plugin => EditorView.atomicRanges.of(view => {
    return view.plugin(plugin)?.placeholders || Decoration.none
  })
})

Dear name,
Your item is on its way. Please see order for details.

It is possible to implement something like that in a custom way with transaction filters, if you need
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Undoable Effects

By default, the history extension only tracks changes to the document and selection, and undoing will only roll back those, not any other part of the editor state.

Sometimes, you do need other actions on that state to be undoable. If you model those actions as state effects, it is possible to wire such functionality into the core history module. The way you do that is by registering your effect to be invertable. When the history sees a transaction with such an effect, it'll store its inverse, and apply that when the transaction is undone.

Let's go through an example of an extension that allows the user to highlight parts of the document, and undo that highlighting.

We'll keep the information about highlighted ranges in a state field, and define effects to add and remove such ranges.

import {StateEffect, ChangeDesc} from "@codemirror/state"

const addHighlight = StateEffect.define<{from: number, to: number}>({
  map: mapRange
})
const removeHighlight = StateEffect.define<{from: number, to: number}>({
  map: mapRange
})

function mapRange(range: {from: number, to: number}, change: ChangeDesc) {
  let from = change.mapPos(range.from), to = change.mapPos(range.to)
  return from < to ? {from, to} : undefined
}

Such effects can be added to a transaction, and inspected up by code looking at the transaction. Since the effect contain document positions, they need to define a mapping function in order to be adjusted properly when, for example, a change is made with the addToHistory flag set to false, causing an undone effect to be applied to a different document than the one it was originally made for.

We define a state field holding a range set of decorations representing the highlighted ranges. Whenever a transaction that with highlighter-related effects comes in, the field's update function applies those effects.

import {Decoration, DecorationSet} from "@codemirror/view"
import {StateField} from "@codemirror/state"

const highlight = Decoration.mark({
  attributes: {style: `background-color: rgba(255, 50, 0, 0.3)`}
})

const highlightedRanges = StateField.define({
  create() {
    return Decoration.none
  },
  update(ranges, tr) {
    ranges = ranges.map(tr.changes)
    for (let e of tr.effects) {
      if (e.is(addHighlight))
        ranges = addRange(ranges, e.value)
      else if (e.is(removeHighlight))
        ranges = cutRange(ranges, e.value)
    }
    return ranges
  },
  provide: field => EditorView.decorations.from(field)
})

In order to make sure our range set doesn't contain overlapping or needlessly fragmented ranges, these helper methods clear or add a range by replacing all the highlights that touch the given range with either a single continuous range, when adding, or only the pieces of the old ranges that stuck out of the cleared region, when removing.

function cutRange(ranges: DecorationSet, r: {from: number, to: number}) {
  let leftover = []
  ranges.between(r.from, r.to, (from, to, deco) => {
    if (from < r.from) leftover.push(deco.range(from, r.from))
    if (to > r.to) leftover.push(deco.range(r.to, to))
  })
  return ranges.update({
    filterFrom: r.from,
    filterTo: r.to,
    filter: () => false,
    add: leftover
  })
}

function addRange(ranges: DecorationSet, r: {from: number, to: number}) {
  ranges.between(r.from, r.to, (from, to) => {
    if (from < r.from) r = {from, to: r.to}
    if (to > r.to) r = {from: r.from, to}
  })
  return ranges.update({
    filterFrom: r.from,
    filterTo: r.to,
    filter: () => false,
    add: [highlight.range(r.from, r.to)]
  })
}

Now we can define our effect-inversion logic. The function we give to invertedEffects is called for every transaction, and returns an array of effects that the history should store alongside the inverse of that transaction.

So this function turns effects that add highlights into effects that remove them, and vice versa.

And because deleting a region around a highlight also deletes the highlight, and we might want to restore them when undoing the deletion, the function also iterates over all replaced ranges and creates a highlight effect for any covered highlight in them.

import {invertedEffects} from "@codemirror/commands"

const invertHighlight = invertedEffects.of(tr => {
  let found = []
  for (let e of tr.effects) {
    if (e.is(addHighlight)) found.push(removeHighlight.of(e.value))
    else if (e.is(removeHighlight)) found.push(addHighlight.of(e.value))
  }
  let ranges = tr.startState.field(highlightedRanges)
  tr.changes.iterChangedRanges((chFrom, chTo) => {
    ranges.between(chFrom, chTo, (rFrom, rTo) => {
      let from = Math.max(chFrom, rFrom), to = Math.min(chTo, rTo)
      if (from < to) found.push(addHighlight.of({from, to}))
    })
  })
  return found
})

These two commands apply our effects to any selected ranges.

import {EditorView} from "@codemirror/view"

function highlightSelection(view: EditorView) {
  view.dispatch({
    effects: view.state.selection.ranges.filter(r => !r.empty)
      .map(r => addHighlight.of(r))
  })
  return true
}

function unhighlightSelection(view: EditorView) {
  let highlighted = view.state.field(highlightedRanges)
  let effects = []
  for (let sel of view.state.selection.ranges) {
    highlighted.between(sel.from, sel.to, (rFrom, rTo) => {
      let from = Math.max(sel.from, rFrom), to = Math.min(sel.to, rTo)
      if (from < to) effects.push(removeHighlight.of({from, to}))
    })
  }
  view.dispatch({effects})
  return true
}

Note that unhighlightSelection only creates effects for previously highlighted ranges that overlap the selection. If we had simply created them for the entire selected ranges, inverting those effects could cause things to be highlighted that were not previously highlighted.

import {keymap} from "@codemirror/view"

const highlightKeymap = keymap.of([
  {key: "Mod-h", run: highlightSelection},
  {key: "Shift-Mod-h", run: unhighlightSelection}
])

export function rangeHighlighting() {
  return [
    highlightedRanges,
    invertHighlight,
    highlightKeymap
  ]
}

If we tie all that together into an extension, it makes an editor behave like this:
1
2
3
4
Select something and press ctrl/cmd-h to highlight it
or shift-ctrl/cmd-h to remove highlighting.
Try undoing and redoing a highlight action.

Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Gutters

The view module provides functionality for adding gutters (vertical bars in front of the code) to your editor. The simplest use of gutters is to simply dump lineNumbers() into your configuration to get a line number gutter. But the module also helps when you want to define your own gutters and show custom controls in them.
Adding a Gutter

Conceptually, the editor displays a collection of gutters next to each other, each of which has its own style and content (though you'll often want to keep their default style so that they blend in with the others, looking like a single big gutter). For each line, each gutter may display something. The line number gutter will show a line number—obviously.

To add a gutter, call the gutter function and include the result in your state configuration. The position of this extension relative to other gutter extensions determines the ordering of the gutters. So this, for example, will put our gutter after the line numbers:

extensions: [lineNumbers(), gutter({class: "cm-mygutter"})]

Unless the cm-mygutter CSS class sets some minimum width, you won't see such a gutter though—it'll just be an empty element (in a CSS flexbox), which the browser will collapse.

To put content into the gutter, we can use either the lineMarker option, which will be called for each visible line to determine what to show there, or the markers option, which allows you to build a persistent set of markers (using the same range set data structure used in decorations) to show in your gutter.

As with decorations, gutter markers are represented by lightweight immutable values that know how to render themselves to DOM nodes, in order to allow updates to be represented in a declarative way without recreating a lot of DOM nodes on every transaction. Gutter markers can also add a CSS class to a gutter element.

This code defines two gutters, one that shows an ø sign on every empty line, and one that allows you to toggle a 'breakpoint' marker per line by clicking on that gutter. The first is easy:

import {EditorView, gutter, GutterMarker} from "@codemirror/view"

const emptyMarker = new class extends GutterMarker {
  toDOM() { return document.createTextNode("ø") }
}

const emptyLineGutter = gutter({
  lineMarker(view, line) {
    return line.from == line.to ? emptyMarker : null
  },
  initialSpacer: () => emptyMarker
})

(The new class construct creates an anonymous class and then initializes a single instance of it. Since there's only one type of empty-line marker, we use this to get our GutterMarker instance.)

To avoid the problem with empty gutters not showing up at all, gutters allow you to configure a 'spacer' element that is rendered invisibly in the gutter to set its minimal width. This is often easier than setting an explicit with with CSS and making sure it covers the expected content.

The lineMarker option checks if the line is zero-length, and if so, returns our marker.

The breakpoint gutter is a bit more involved. It needs to track state (the position of the breakpoints), for which we use a state field, with a state effect that can update it.

import {StateField, StateEffect, RangeSet} from "@codemirror/state"

const breakpointEffect = StateEffect.define<{pos: number, on: boolean}>({
  map: (val, mapping) => ({pos: mapping.mapPos(val.pos), on: val.on})
})

const breakpointState = StateField.define<RangeSet<GutterMarker>>({
  create() { return RangeSet.empty },
  update(set, transaction) {
    set = set.map(transaction.changes)
    for (let e of transaction.effects) {
      if (e.is(breakpointEffect)) {
        if (e.value.on)
          set = set.update({add: [breakpointMarker.range(e.value.pos)]})
        else
          set = set.update({filter: from => from != e.value.pos})
      }
    }
    return set
  }
})

function toggleBreakpoint(view: EditorView, pos: number) {
  let breakpoints = view.state.field(breakpointState)
  let hasBreakpoint = false
  breakpoints.between(pos, pos, () => {hasBreakpoint = true})
  view.dispatch({
    effects: breakpointEffect.of({pos, on: !hasBreakpoint})
  })
}

The state starts empty, and when a transaction happens, it maps the positions of the breakpoints through the changes (if any), and looks for effects that add or remove breakpoints, adjusting the set of breakpoints as appropriate.

The breakpointGutter extension combines this state field with a gutter and a bit of styling for that gutter.

const breakpointMarker = new class extends GutterMarker {
  toDOM() { return document.createTextNode("💔") }
}

const breakpointGutter = [
  breakpointState,
  gutter({
    class: "cm-breakpoint-gutter",
    markers: v => v.state.field(breakpointState),
    initialSpacer: () => breakpointMarker,
    domEventHandlers: {
      mousedown(view, line) {
        toggleBreakpoint(view, line.from)
        return true
      }
    }
  }),
  EditorView.baseTheme({
    ".cm-breakpoint-gutter .cm-gutterElement": {
      color: "red",
      paddingLeft: "5px",
      cursor: "default"
    }
  })
]

The domEventHandlers option allows you to specify event handlers for this gutter, which we use to set up a mousedown handler to toggle the breakpoint for the line that was clicked.

This is what an editor with the breakpoint gutter before the line numbers and the empty line gutter after it looks like:
1
2
3
4
5
6
7
8
9
ø
ø
ø
Some
text
with

blank

lines
.

Customizing the Line Number Gutter

The lineNumbers function also takes configuration parameters, allowing you to add event handlers or customize the way line numbers are displayed.

const hexLineNumbers = lineNumbers({
  formatNumber: n => n.toString(16)
})

It is also possible to add markers to the line number gutter, which replace the line numbers for affected lines. This is done through the lineNumberMarkers facet, which works a lot like markers on custom gutters, but can be provided by any extension, rather than being configured directly for a single gutter.
Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Autocompletion

The @codemirror/autocomplete package provides functionality for displaying input suggestions in the editor. This example shows how to enable it and how to write your own completion sources.
Setting Up

Autocompletion is enabled by including the autocompletion extension (which is included in the basic setup) in your configuration. Some language packages come with support for proper autocompletion built in, such as the HTML package.
1
2
3
4
⌄
<!doctype html>
<html>
  
</html>

By default, the plugin will look for completions whenever the user types something, but you can configure it to only run when activated explicitly via a command.

The default completion keymap binds Ctrl-Space to start completion, arrows to select a completion, Enter to pick it, and Escape to close the tooltip. It is activated by default when you add the extension, but you can disable that if you want to provide your own bindings.

The default bindings do not bind Tab to acceptCompletion, for reasons outlined in the Tab-handling example.
Providing Completions

The completions that the extension shows come from one or more completion sources, which are functions that take a completion context—an object with information about the completion being requested—and return an object that describes the range that's being completed and the options to show. Sources may run asynchronously by returning a promise.

The easiest way to connect a completion source to an editor is to use the override option.
1
2
Press Ctrl-Space in here...

This editor uses the following completion function:

import {CompletionContext} from "@codemirror/autocomplete"

function myCompletions(context: CompletionContext) {
  let word = context.matchBefore(/\w*/)
  if (word.from == word.to && !context.explicit)
    return null
  return {
    from: word.from,
    options: [
      {label: "match", type: "keyword"},
      {label: "hello", type: "variable", info: "(World)"},
      {label: "magic", type: "text", apply: "⠁⭒*.✩.*⭒⠁", detail: "macro"}
    ]
  }
}

This is a very crude way to provide completions, without really looking at the editing context at all. But it demonstrates the basic things a completion function must do.

    Figure out which bit of text before the cursor could be completed. Here we use the matchBefore method to determine it with a regular expression.

    Check whether completion is appropriate at all. The explicit flag indicates whether the completion was started explicitly, via the command, or implicitly, by typing. You should generally only return results when the completion happens explicitly or the completion position is after some construct that can be completed.

    Build up a list of completions and return it, along with its start position. (The end position defaults to the position where completion happens.)

Completions themselves are objects with a label property, which provides both the text to show in the options list and the text to insert when the completion is picked.

By default, the completion list shows only the label. You'll usually also want to provide a type property, which determines the icon shown next to the completion. detail can be given to show a short string after the label, and info can be used for longer text, shown in a window to the side of the list when the completion is selected.

To override what happens when a completion is picked, you can use the apply property, which can be either a string to replace the completion range with, or a function that will be called to apply an arbitrary action.

When you are providing your completion source as a generic extension, or working with mixed-language documents, setting a global source is not practical. When no override is given, the plugin uses EditorState.languageDataAt with the name "autocomplete" to look up language-appropriate completion functions. Registering those is done with a language object's data facet. For example by including something like this in your state configuration:

myLanguage.data.of({
  autocomplete: myCompletions
})

You can also directly put an array of completion objects in this property, which will cause the library to simply use those (wrapped by completeFromList) as a source.
Sorting and Filtering

The trivial completion source used above didn't have to filter completions against the input—the plugin will take care of that. It uses a form of fuzzy matching to filter and rank completions against the currently typed text, and will highlight the letters in each completion that match.

To influence the ranking of completions, you can give completion objects a boost property, which adds to or subtracts from their match score.

If you really do want to filter and order completions yourself, you can include a filter: false property in your result object to disable the built-in filtering.
Completion Result Validity

Some sources need to recompute their results on every keypress, but for many of them, this is unnecessary and inefficient. They return a full list of completions for a given construct, and as long as the user is typing (or backspacing) inside that construct, that same list can be used (filtered for the currently typed input) to populate the completion list.

This is why it is very much recommended to provide a validFor property on your completion result. It should contain a function or regular expression that tells the extension that, as long as the updated input (the range between the result's from property and the completion point) matches that value, it can continue to use the list of completions.

In the myCompletions function above, since all its completions are simple words, a value like validFor: /^\w*$/ would be appropriate.
Completing from Syntax

To make a completion source a bit more intelligent, it is often useful to inspect the syntax tree around the completion point, and use that to get a better picture of what kind of construct is being completed.

As an example, this completion source for JavaScript will complete (some) JSDoc tags in block comments.

import {syntaxTree} from "@codemirror/language"

const tagOptions = [
  "constructor", "deprecated", "link", "param", "returns", "type"
].map(tag => ({label: "@" + tag, type: "keyword"}))

function completeJSDoc(context: CompletionContext) {
  let nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1)
  if (nodeBefore.name != "BlockComment" ||
      context.state.sliceDoc(nodeBefore.from, nodeBefore.from + 3) != "/**")
    return null
  let textBefore = context.state.sliceDoc(nodeBefore.from, context.pos)
  let tagBefore = /@\w*$/.exec(textBefore)
  if (!tagBefore && !context.explicit) return null
  return {
    from: tagBefore ? nodeBefore.from + tagBefore.index : context.pos,
    options: tagOptions,
    validFor: /^(@\w*)?$/
  }
}

The function starts by finding the syntax node directly in front of the completion position. If that is not a block comment, or it is a block comment without a /** start marker, it returns null to indicate it has no completions.

If the completion does happen in a block comment, we check whether there is an existing tag in front of it. If there is, that is included in the completion (see the from property in the returned object). If there isn't, we only complete if the completion was explicitly started.

You can now use an extension like this to enable this completion source for JavaScript content.

import {javascriptLanguage} from "@codemirror/lang-javascript"

const jsDocCompletions = javascriptLanguage.data.of({
  autocomplete: completeJSDoc
})

Try it out:
1
2
3
4
⌄
/** Complete tags here
    @pa
 */

Code of Conduct
Report an Issue

CodeMirror
Examples
Documentation
Try
Discuss
GitHub
Version 5
Example: Linter

The @codemirror/lint package provides a way to display errors and warnings in your editor. If you give it a source function that, when given an editor, produces an array of problems, it will call this function when changes are made to the document, and display its result.

The library does not come with a collection of lint sources. Some language packages (such as @codemirror/lang-javascript) may include integration with lint libraries, but usually setting up a source is something you have to do yourself.

There's two ways to do this:

    Find a linter that you can run in the browser, run it on the content, and translate its output into the expected format.

    Write one from scratch, possibly using the syntax tree kept by the editor.

In this example, we'll do the latter.

Assume for a second that the boss has decided that regular expressions are the work of the devil (to be fair, he's not entirely wrong), and should be forbidden in the entire codebase. We want a linter that highlights any use of a regular expression in JavaScript code.

Conveniently, the JavaScript parser emits a specific node type for regular expression literals, so all we have to do is iterate over the parse tree and emit a warning for every node like that we find.

import {syntaxTree} from "@codemirror/language"
import {linter, Diagnostic} from "@codemirror/lint"

const regexpLinter = linter(view => {
  let diagnostics: Diagnostic[] = []
  syntaxTree(view.state).cursor().iterate(node => {
    if (node.name == "RegExp") diagnostics.push({
      from: node.from,
      to: node.to,
      severity: "warning",
      message: "Regular expressions are FORBIDDEN",
      actions: [{
        name: "Remove",
        apply(view, from, to) { view.dispatch({changes: {from, to}}) }
      }]
    })
  })
  return diagnostics
})

Diagnostics (the objects returned by a lint source) must have from and to properties indicating the range they apply to, a severity field, and a message.

This one also uses the optional actions field to add an “action” for the diagnostic. This adds a button to the diagnostic that can be clicked to perform some effect, like automatically fixing the problem (which this does in a very crude way) or providing further context.

The result of a call to linter is an extension that you can include in a (JavaScript) editor to get a result like this:
1
2
3
⌄
function isNumber(string) {
  return /^\d+(\.\d*)?$/.test(string)
}

The gutter only appears if you also include lintGutter. You can press Ctrl-Shift-m (Cmd-Shift-m on macOS) to show a list of diagnostics in a panel below the editor.
Code of Conduct
Report an Issue

""".







some useful libs:



"""

https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/keymap/emacs.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/keymap/sublime.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/keymap/vim.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/meta.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/theme/3024-night.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/theme/duotone-dark.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/comment/comment.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/comment/continuecomment.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/dialog/dialog.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/dialog/dialog.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/autorefresh.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/fullscreen.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/fullscreen.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/panel.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/placeholder.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/rulers.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/closebrackets.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/closetag.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/continuelist.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/matchbrackets.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/matchtags.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/edit/trailingspace.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/brace-fold.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/comment-fold.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldcode.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldgutter.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/foldgutter.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/indent-fold.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/markdown-fold.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/fold/xml-fold.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/anyword-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/css-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/html-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/javascript-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/show-hint.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/show-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/sql-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/hint/xml-hint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/coffeescript-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/css-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/html-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/json-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/yaml-lint.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/merge/merge.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/merge/merge.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/mode/loadmode.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/mode/multiplex.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/mode/multiplex_test.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/mode/overlay.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/mode/simple.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/runmode/colorize.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/runmode/runmode-standalone.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/runmode/runmode.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/runmode/runmode.node.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/scroll/annotatescrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/scroll/scrollpastend.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/scroll/simplescrollbars.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/scroll/simplescrollbars.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/jump-to-line.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/match-highlighter.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/matchesonscrollbar.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/matchesonscrollbar.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/search.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/search/searchcursor.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/selection/active-line.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/selection/mark-selection.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/selection/selection-pointer.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/tern/tern.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/tern/tern.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/tern/worker.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/wrap/hardwrap.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/apl/apl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/asciiarmor/asciiarmor.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/asn.1/asn.1.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/asterisk/asterisk.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/brainfuck/brainfuck.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/clike/clike.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/clojure/clojure.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/cmake/cmake.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/cobol/cobol.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/coffeescript/coffeescript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/commonlisp/commonlisp.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/crystal/crystal.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/css/css.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/cypher/cypher.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/d/d.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/dart/dart.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/diff/diff.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/django/django.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/dockerfile/dockerfile.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/dtd/dtd.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/dylan/dylan.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ebnf/ebnf.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ecl/ecl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/eiffel/eiffel.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/elm/elm.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/erlang/erlang.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/factor/factor.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/fcl/fcl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/forth/forth.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/fortran/fortran.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/gas/gas.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/gfm/gfm.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/gherkin/gherkin.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/go/go.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/groovy/groovy.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/haml/haml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/handlebars/handlebars.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/haskell-literate/haskell-literate.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/haskell/haskell.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/haxe/haxe.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/htmlembedded/htmlembedded.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/htmlmixed/htmlmixed.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/http/http.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/idl/idl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/javascript/javascript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/jinja2/jinja2.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/jsx/jsx.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/julia/julia.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/livescript/livescript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/lua/lua.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/markdown/markdown.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mathematica/mathematica.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mbox/mbox.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mirc/mirc.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mllike/mllike.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/modelica/modelica.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mscgen/mscgen.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/mumps/mumps.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/nginx/nginx.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/nsis/nsis.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ntriples/ntriples.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/octave/octave.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/oz/oz.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/pascal/pascal.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/pegjs/pegjs.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/perl/perl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/php/php.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/pig/pig.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/powershell/powershell.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/properties/properties.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/protobuf/protobuf.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/pug/pug.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/puppet/puppet.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/python/python.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/q/q.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/r/r.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/rpm/rpm.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/rst/rst.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ruby/ruby.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/rust/rust.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/sas/sas.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/sass/sass.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/scheme/scheme.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/shell/shell.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/sieve/sieve.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/slim/slim.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/smalltalk/smalltalk.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/smarty/smarty.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/solr/solr.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/soy/soy.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/sparql/sparql.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/spreadsheet/spreadsheet.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/sql/sql.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/stex/stex.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/stylus/stylus.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/swift/swift.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tcl/tcl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/textile/textile.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tiddlywiki/tiddlywiki.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tiddlywiki/tiddlywiki.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tiki/tiki.min.css
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tiki/tiki.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/toml/toml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/tornado/tornado.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/troff/troff.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ttcn-cfg/ttcn-cfg.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/ttcn/ttcn.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/turtle/turtle.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/twig/twig.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/vb/vb.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/vbscript/vbscript.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/velocity/velocity.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/verilog/verilog.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/vhdl/vhdl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/vue/vue.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/wast/wast.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/webidl/webidl.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/xml/xml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/xquery/xquery.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/yacas/yacas.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/yaml-frontmatter/yaml-frontmatter.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/yaml/yaml.min.js
https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/z80/z80.min.js


""".




YOU JUST OVERPIZDEFUCKEYED IT!:


"""

Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/2e6985de-4ddd-464e-a5a1-09c274328296:114
Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    timeout https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    R https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    setOption https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/2e6985de-4ddd-464e-a5a1-09c274328296:168
Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. 4 javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    timeout https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
Error: window.CSSLint not defined, CodeMirror CSS linting cannot run. css-lint.min.js:1:322
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/css-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/d150c8f4-f2ff-4e4f-9ac1-8ced31e9cd9a:168
Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/d150c8f4-f2ff-4e4f-9ac1-8ced31e9cd9a:178
Error: window.CSSLint not defined, CodeMirror CSS linting cannot run. css-lint.min.js:1:322
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/css-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    timeout https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run. javascript-lint.min.js:1:319
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/javascript-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    timeout https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
Error: window.CSSLint not defined, CodeMirror CSS linting cannot run. css-lint.min.js:1:322
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/css-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/edb714da-093c-4460-b8ab-2bdba00c568c:151
Uncaught TypeError: HTMLHint.verify is not a function
    lintEditor blob:https://yuntian-deng-chatgpt4.hf.space/94f49d20-432c-4a21-b7de-2228caff9054:161
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/94f49d20-432c-4a21-b7de-2228caff9054:183
94f49d20-432c-4a21-b7de-2228caff9054:161:27
    lintEditor blob:https://yuntian-deng-chatgpt4.hf.space/94f49d20-432c-4a21-b7de-2228caff9054:161
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/94f49d20-432c-4a21-b7de-2228caff9054:183
Uncaught SyntaxError: `` literal not terminated before end of script
The resource from “https://cdnjs.cloudflare.com/ajax/libs/htmlhint/1.1.4/htmlhint.min.js” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff).
Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run. html-lint.min.js:1:632
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/html-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/4c4069e1-b3b1-44b7-ba81-dab5e61a9582:123
Uncaught TypeError: HTMLHint.verify is not a function
    getAnnotations blob:https://yuntian-deng-chatgpt4.hf.space/42ce50b1-81ad-4618-b185-d118a1b80e2f:119
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/42ce50b1-81ad-4618-b185-d118a1b80e2f:112
42ce50b1-81ad-4618-b185-d118a1b80e2f:119:36
    getAnnotations blob:https://yuntian-deng-chatgpt4.hf.space/42ce50b1-81ad-4618-b185-d118a1b80e2f:119
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/42ce50b1-81ad-4618-b185-d118a1b80e2f:112

The resource from “https://cdn.jsdelivr.net/npm/eslint@8.48.0/dist/eslint.min.js” was blocked due to MIME type (“text/plain”) mismatch (X-Content-Type-Options: nosniff).

Uncaught TypeError: s.verify is not a function
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/html-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/273c2366-acc4-4d35-bd49-5e4797223fec:147
html-lint.min.js:1:782
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/html-lint.min.js:1
    s https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    <anonymous> https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/addon/lint/lint.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    p https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    fromTextArea https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.10/codemirror.min.js:1
    <anonymous> blob:https://yuntian-deng-chatgpt4.hf.space/273c2366-acc4-4d35-bd49-5e4797223fec:147
The resource from “https://cdnjs.cloudflare.com/ajax/libs/htmlhint/1.1.4/htmlhint.min.js” was blocked due to MIME type (“text/html”) mismatch (X-Content-Type-Options: nosniff). 9015103e-2825-4588-8450-926907295514


""".

NEED EXTREME BEYOND FULLY FLEDGE CODE HTML/CSS/JAVASCRIPT EDITOR MULTIUNIVERSALIS IN ONE CODE HTML IN CHAT VERSION OVER9K


SUPER CRAZY DETAILED AND ROBUST LINTING FOR HTML IS IMPORTANT!!!








/**
 * Minified by jsDelivr using Terser v5.19.2.
 * Original file: /npm/htmllint@0.8.0/lib/index.js
 *
 * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
 */
var lodash=require("lodash"),Linter=require("./linter");Object.values=Object.values||function(t){return Object.keys(t).reduce((function(e,l){return e.push(t[l]),e}),[])};var htmllint=function(){var t=htmllint.defaultLinter;return t.lint.apply(t,arguments)};module.exports=htmllint,htmllint.Linter=Linter,htmllint.rules=require("./rules"),htmllint.messages=require("./messages"),htmllint.defaultLinter=new Linter(htmllint.rules),htmllint.use=function(t){t.forEach((function(t){lodash.isString(t)&&(t=require(t)),htmllint.defaultLinter.use(t)}))};
//# sourceMappingURL=/sm/e3c931db5fb8fb2dddec2b7fa99640ad6472490b8f8c682c1f22f448c7bfe84d.map


Uncaught TypeError: htmllint is not a function

https://cdn.jsdelivr.net/npm/htmllint@0.8.0/lib/index.min.js

https://cdn.jsdelivr.net/npm/htmlhint@1.1.4/dist/htmlhint.min.js

https://cdn.jsdelivr.net/npm/eslint@9.17.0/lib/api.min.js

https://cdn.jsdelivr.net/npm/csslint@1.0.5/dist/csslint.min.js

https://cdn.jsdelivr.net/npm/esprima@4.0.1/dist/esprima.min.js


ALSO, DO PROPER CODE FIELDS EDITABLE AND WITH DEFAULT DECENT CODE EXAMPLES.