Lawrence  Lesch

Lawrence Lesch

1678870808

React-codemirror: CodeMirror 6 component for React

React-codemirror

CodeMirror component for React. Demo Preview: @uiwjs.github.io/react-codemirror

Features:

🚀 Quickly and easily configure the API.
🌱 Versions after @uiw/react-codemirror@v4 use codemirror 6. #88.
⚛️ Support the features of React Hook(requires React 16.8+).
📚 Use Typescript to write, better code hints.
🌐 The bundled version supports use directly in the browser #267.
🌎 There are better sample previews.
🎨 Support theme customization, provide theme editor.

Install

Not dependent on uiw.

npm install @uiw/react-codemirror --save

Usage

Open in CodeSandbox

import React from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';

function App() {
  const onChange = React.useCallback((value, viewUpdate) => {
    console.log('value:', value);
  }, []);
  return (
    <CodeMirror
      value="console.log('hello world!');"
      height="200px"
      extensions={[javascript({ jsx: true })]}
      onChange={onChange}
    />
  );
}
export default App;

Support Language

Open in CodeSandbox

import CodeMirror from '@uiw/react-codemirror';
import { StreamLanguage } from '@codemirror/language';
import { go } from '@codemirror/legacy-modes/mode/go';

const goLang = `package main
import "fmt"

func main() {
  fmt.Println("Hello, 世界")
}`;

export default function App() {
  return <CodeMirror value={goLang} height="200px" extensions={[StreamLanguage.define(go)]} />;
}

Markdown Example

Markdown language code is automatically highlighted.

Open in CodeSandbox

import CodeMirror from '@uiw/react-codemirror';
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
import { languages } from '@codemirror/language-data';

const code = `## Title

\`\`\`jsx
function Demo() {
  return <div>demo</div>
}
\`\`\`

\`\`\`bash
# Not dependent on uiw.
npm install @codemirror/lang-markdown --save
npm install @codemirror/language-data --save
\`\`\`

[weisit ulr](https://uiwjs.github.io/react-codemirror/)

\`\`\`go
package main
import "fmt"
func main() {
  fmt.Println("Hello, 世界")
}
\`\`\`
`;

export default function App() {
  return <CodeMirror value={code} extensions={[markdown({ base: markdownLanguage, codeLanguages: languages })]} />;
}

Support Hook

Open in CodeSandbox

import { useEffect, useMemo, useRef } from 'react';
import { useCodeMirror } from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';

const code = "console.log('hello world!');\n\n\n";
// Define the extensions outside the component for the best performance.
// If you need dynamic extensions, use React.useMemo to minimize reference changes
// which cause costly re-renders.
const extensions = [javascript()];

export default function App() {
  const editor = useRef();
  const { setContainer } = useCodeMirror({
    container: editor.current,
    extensions,
    value: code,
  });

  useEffect(() => {
    if (editor.current) {
      setContainer(editor.current);
    }
  }, [editor.current]);

  return <div ref={editor} />;
}

Using Theme

We have created a theme editor where you can define your own theme. We have also defined some themes ourselves, which can be installed and used directly. Below is a usage example:

import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { okaidia } from '@uiw/codemirror-theme-okaidia';

const extensions = [javascript({ jsx: true })];

export default function App() {
  return (
    <CodeMirror
      value="console.log('hello world!');"
      height="200px"
      theme={okaidia}
      extensions={[javascript({ jsx: true })]}
    />
  );
}

Using custom theme

import CodeMirror from '@uiw/react-codemirror';
import { createTheme } from '@uiw/codemirror-themes';
import { javascript } from '@codemirror/lang-javascript';
import { tags as t } from '@lezer/highlight';

const myTheme = createTheme({
  theme: 'light',
  settings: {
    background: '#ffffff',
    foreground: '#75baff',
    caret: '#5d00ff',
    selection: '#036dd626',
    selectionMatch: '#036dd626',
    lineHighlight: '#8a91991a',
    gutterBackground: '#fff',
    gutterForeground: '#8a919966',
  },
  styles: [
    { tag: t.comment, color: '#787b8099' },
    { tag: t.variableName, color: '#0080ff' },
    { tag: [t.string, t.special(t.brace)], color: '#5c6166' },
    { tag: t.number, color: '#5c6166' },
    { tag: t.bool, color: '#5c6166' },
    { tag: t.null, color: '#5c6166' },
    { tag: t.keyword, color: '#5c6166' },
    { tag: t.operator, color: '#5c6166' },
    { tag: t.className, color: '#5c6166' },
    { tag: t.definition(t.typeName), color: '#5c6166' },
    { tag: t.typeName, color: '#5c6166' },
    { tag: t.angleBracket, color: '#5c6166' },
    { tag: t.tagName, color: '#5c6166' },
    { tag: t.attributeName, color: '#5c6166' },
  ],
});
const extensions = [javascript({ jsx: true })];

export default function App() {
  const onChange = React.useCallback((value, viewUpdate) => {
    console.log('value:', value);
  }, []);
  return (
    <CodeMirror
      value="console.log('hello world!');"
      height="200px"
      theme={myTheme}
      extensions={extensions}
      onChange={onChange}
    />
  );
}

Use initialState to restore state from JSON-serialized representation

CodeMirror allows to serialize editor state to JSON representation with toJSON function for persistency or other needs. This JSON representation can be later used to recreate ReactCodeMirror component with the same internal state.

For example, this is how undo history can be saved in the local storage, so that it remains after the page reloads

import CodeMirror from '@uiw/react-codemirror';
import { historyField } from '@codemirror/commands';

// When custom fields should be serialized, you can pass them in as an object mapping property names to fields.
// See [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) documentation for more details
const stateFields = { history: historyField };

export function EditorWithInitialState() {
  const serializedState = localStorage.getItem('myEditorState');
  const value = localStorage.getItem('myValue') || '';

  return (
    <CodeMirror
      value={value}
      initialState={
        serializedState
          ? {
              json: JSON.parse(serializedState || ''),
              fields: stateFields,
            }
          : undefined
      }
      onChange={(value, viewUpdate) => {
        localStorage.setItem('myValue', value);

        const state = viewUpdate.state.toJSON(stateFields);
        localStorage.setItem('myEditorState', JSON.stringify(state));
      }}
    />
  );
}

Props

  • value?: string value of the auto created model in the editor.
  • width?: string width of editor. Defaults to auto.
  • height?: string height of editor. Defaults to auto.
  • theme?: 'light' / 'dark' / Extension Defaults to 'light'.
import React from 'react';
import { EditorState, EditorStateConfig, Extension } from '@codemirror/state';
import { EditorView, ViewUpdate } from '@codemirror/view';
export * from '@codemirror/view';
export * from '@codemirror/basic-setup';
export * from '@codemirror/state';
export interface UseCodeMirror extends ReactCodeMirrorProps {
  container?: HTMLDivElement | null;
}
export declare function useCodeMirror(props: UseCodeMirror): {
  state: EditorState | undefined;
  setState: import('react').Dispatch<import('react').SetStateAction<EditorState | undefined>>;
  view: EditorView | undefined;
  setView: import('react').Dispatch<import('react').SetStateAction<EditorView | undefined>>;
  container: HTMLDivElement | null | undefined;
  setContainer: import('react').Dispatch<import('react').SetStateAction<HTMLDivElement | null | undefined>>;
};
export interface ReactCodeMirrorProps
  extends Omit<EditorStateConfig, 'doc' | 'extensions'>,
    Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'placeholder'> {
  /** value of the auto created model in the editor. */
  value?: string;
  height?: string;
  minHeight?: string;
  maxHeight?: string;
  width?: string;
  minWidth?: string;
  maxWidth?: string;
  /** focus on the editor. */
  autoFocus?: boolean;
  /** Enables a placeholder—a piece of example content to show when the editor is empty. */
  placeholder?: string | HTMLElement;
  /**
   * `light` / `dark` / `Extension` Defaults to `light`.
   * @default light
   */
  theme?: 'light' | 'dark' | Extension;
  /**
   * Whether to optional basicSetup by default
   * @default true
   */
  basicSetup?: boolean | BasicSetupOptions;
  /**
   * This disables editing of the editor content by the user.
   * @default true
   */
  editable?: boolean;
  /**
   * This disables editing of the editor content by the user.
   * @default false
   */
  readOnly?: boolean;
  /**
   * Whether to optional basicSetup by default
   * @default true
   */
  indentWithTab?: boolean;
  /** Fired whenever a change occurs to the document. */
  onChange?(value: string, viewUpdate: ViewUpdate): void;
  /** Some data on the statistics editor. */
  onStatistics?(data: Statistics): void;
  /** The first time the editor executes the event. */
  onCreateEditor?(view: EditorView, state: EditorState): void;
  /** Fired whenever any state change occurs within the editor, including non-document changes like lint results. */
  onUpdate?(viewUpdate: ViewUpdate): void;
  /**
   * Extension values can be [provided](https://codemirror.net/6/docs/ref/#state.EditorStateConfig.extensions) when creating a state to attach various kinds of configuration and behavior information.
   * They can either be built-in extension-providing objects,
   * such as [state fields](https://codemirror.net/6/docs/ref/#state.StateField) or [facet providers](https://codemirror.net/6/docs/ref/#state.Facet.of),
   * or objects with an extension in its `extension` property. Extensions can be nested in arrays arbitrarily deep—they will be flattened when processed.
   */
  extensions?: Extension[];
  /**
   * If the view is going to be mounted in a shadow root or document other than the one held by the global variable document (the default), you should pass it here.
   * Originally from the [config of EditorView](https://codemirror.net/6/docs/ref/#view.EditorView.constructor%5Econfig.root)
   */
  root?: ShadowRoot | Document;
  /**
   * Create a state from its JSON representation serialized with [toJSON](https://codemirror.net/docs/ref/#state.EditorState.toJSON) function
   */
  initialState?: {
    json: any;
    fields?: Record<'string', StateField<any>>;
  };
}
export interface ReactCodeMirrorRef {
  editor?: HTMLDivElement | null;
  state?: EditorState;
  view?: EditorView;
}
declare const ReactCodeMirror: React.ForwardRefExoticComponent<
  ReactCodeMirrorProps & React.RefAttributes<ReactCodeMirrorRef>
>;
export default ReactCodeMirror;
export interface BasicSetupOptions {
  lineNumbers?: boolean;
  highlightActiveLineGutter?: boolean;
  highlightSpecialChars?: boolean;
  history?: boolean;
  foldGutter?: boolean;
  drawSelection?: boolean;
  dropCursor?: boolean;
  allowMultipleSelections?: boolean;
  indentOnInput?: boolean;
  syntaxHighlighting?: boolean;
  bracketMatching?: boolean;
  closeBrackets?: boolean;
  autocompletion?: boolean;
  rectangularSelection?: boolean;
  crosshairCursor?: boolean;
  highlightActiveLine?: boolean;
  highlightSelectionMatches?: boolean;
  closeBracketsKeymap?: boolean;
  defaultKeymap?: boolean;
  searchKeymap?: boolean;
  historyKeymap?: boolean;
  foldKeymap?: boolean;
  completionKeymap?: boolean;
  lintKeymap?: boolean;
}
import { EditorSelection, SelectionRange } from '@codemirror/state';
import { ViewUpdate } from '@codemirror/view';
export interface Statistics {
  /** Get the number of lines in the editor. */
  lineCount: number;
  /** total length of the document */
  length: number;
  /** Get the proper [line-break](https://codemirror.net/docs/ref/#state.EditorState^lineSeparator) string for this state. */
  lineBreak: string;
  /** Returns true when the editor is [configured](https://codemirror.net/6/docs/ref/#state.EditorState^readOnly) to be read-only. */
  readOnly: boolean;
  /** The size (in columns) of a tab in the document, determined by the [`tabSize`](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize) facet. */
  tabSize: number;
  /** Cursor Position */
  selection: EditorSelection;
  /** Make sure the selection only has one range. */
  selectionAsSingle: SelectionRange;
  /** Retrieves a list of all current selections. */
  ranges: readonly SelectionRange[];
  /** Get the currently selected code. */
  selectionCode: string;
  /**
   * The length of the given array should be the same as the number of active selections.
   * Replaces the content of the selections with the strings in the array.
   */
  selections: string[];
  /** Return true if any text is selected. */
  selectedText: boolean;
}
export declare const getStatistics: (view: ViewUpdate) => Statistics;

All Packages

NameNPM VersionWebsite
@uiw/react-codemirrornpm version NPM Downloads#preview
@uiw/codemirror-extensions-basic-setupnpm version NPM Downloads#preview
@uiw/codemirror-extensions-colornpm version NPM Downloads#preview
@uiw/codemirror-extensions-classnamenpm version NPM Downloads#preview
@uiw/codemirror-extensions-eventsnpm version NPM Downloads#preview
@uiw/codemirror-extensions-hyper-linknpm version NPM Downloads#preview
@uiw/codemirror-extensions-langsnpm version NPM Downloads#preview
@uiw/codemirror-extensions-line-numbers-relativenpm version NPM Downloads#preview
@uiw/codemirror-extensions-mentionsnpm version NPM Downloads#preview
@uiw/codemirror-extensions-zebra-stripesnpm version NPM Downloads#preview
@uiw/codemirror-themesnpm version NPM Downloads#preview
NameNPM VersionWebsite
@uiw/codemirror-themes-allnpm version NPM Downloads#preview
@uiw/codemirror-theme-abcdefnpm version NPM Downloads#preview
@uiw/codemirror-theme-androidstudionpm version NPM Downloads#preview
@uiw/codemirror-theme-atomonenpm version NPM Downloads#preview
@uiw/codemirror-theme-auranpm version NPM Downloads#preview
@uiw/codemirror-theme-bbeditnpm version NPM Downloads#preview
@uiw/codemirror-theme-bespinnpm version NPM Downloads#preview
@uiw/codemirror-theme-duotonenpm version NPM Downloads#preview
@uiw/codemirror-theme-draculanpm version NPM Downloads#preview
@uiw/codemirror-theme-darculanpm version NPM Downloads#preview
@uiw/codemirror-theme-eclipsenpm version NPM Downloads#preview
@uiw/codemirror-theme-githubnpm version NPM Downloads#preview
@uiw/codemirror-theme-gruvbox-darknpm version NPM Downloads#preview
@uiw/codemirror-theme-materialnpm version NPM Downloads#preview
@uiw/codemirror-theme-noctis-lilacnpm version NPM Downloads#preview
@uiw/codemirror-theme-nordnpm version NPM Downloads#preview
@uiw/codemirror-theme-okaidianpm version NPM Downloads#preview
@uiw/codemirror-theme-solarizednpm version NPM Downloads#preview
@uiw/codemirror-theme-sublimenpm version NPM Downloads#preview
@uiw/codemirror-theme-tokyo-nightnpm version NPM Downloads#preview
@uiw/codemirror-theme-tokyo-night-stormnpm version NPM Downloads#preview
@uiw/codemirror-theme-tokyo-night-daynpm version NPM Downloads#preview
@uiw/codemirror-theme-vscodenpm version NPM Downloads#preview
@uiw/codemirror-theme-xcodenpm version NPM Downloads#preview

Related


Download Details:

Author: uiwjs
Source Code: https://github.com/uiwjs/react-codemirror 
License: MIT license

#typescript #react #editor #hook #codemirror 

React-codemirror: CodeMirror 6 component for React
Lawrence  Lesch

Lawrence Lesch

1675858289

HyperMD: A WYSIWYG Markdown Editor for Browsers

HyperMD

Breaks the Wall between writing and preview, in a Markdown Editor.

Quickstart

// npm install --save hypermd codemirror
var HyperMD = require('hypermd')
var myTextarea = document.getElementById('input-area')
var editor = HyperMD.fromTextArea(myTextarea)

Remix on Glitch

Also for RequireJS, Parcel, webpack, plain browser env. Read the Doc

Why use HyperMD?

HyperMD is a set of CodeMirror add-ons / modes / themes / commands / keymap etc.

You may use both original CodeMirror and HyperMD on the same page.

🌈 Write, and preview on the fly

  • Regular Markdown blocks and elements
    • Strong, Emphasis, Strikethrough, Code
    • Links, Images
    • Title / Quote / Code Block / List / Horizontal Rule
  • Markdown Extension
    • Simple Table
    • Footnote [^1]
    •  TODO List (the box is clickable)
    • YAML Front Matter
    • $\LaTeX$ Formula, inline or block display mode [^4]
    • Emoji: :joy: => :joy: (also support custom emoji)
  • And more
    • HTML in Markdown -- WYSIWIG MDX is possible
    • #hashtag support [^6] , see demo
    • Flowchart and Diagrams (mermaid or flowchart.js)

💪 Better Markdown-ing Experience

  • Upload Images and files via clipboard, or drag'n'drop
  • Alt+Click to open link / jump to footnote [^1]
  • Hover to read footnotes
  • Copy and Paste, translate HTML into Markdown [^5]
  • Easy and ready-to-use Key-bindings (aka. KeyMap)

🎁 CodeMirror benefits

  • Syntax Highlight for code blocks, supports 120+ languages[^2]. Mode can be loaded on-demand.
  • Configurable key-bindings
  • Diff and Merge
  • Themes [^3]
  • Almost all of CodeMirror addons!

🔨 Extensible and Customizable

🎹 Tailored KeyMap "HyperMD":

  • Table
    • Enter Create a table with | column | line |
    • Enter Insert new row, or finish a table (if last row is empty)
    • Tab or Shift-Tab to navigate between cells
  • List
    • Tab or Shift-Tab to indent/unindent current list item
  • Formatting a nearby word (or selected text)
    • Ctrl+B bold
    • Ctrl+I emphasis
    • Ctrl+D strikethrough

Special Thanks

💎 Service and Resource

IcoMoon - The IconPack(Free Version)
Demo Page uses IcoMoon icons. Related files are stored in demo/svgicon
CodeCogs - An Open Source Scientific Library
FoldMath uses codecogs' service as the default TeX MathRenderer.
(You may load PowerPack to use other renderer, eg. KaTeX or MathJax) 
SM.MS - A Free Image Hosting service
Demo Page and PowerPack/insert-file-with-smms use SM.MS open API to upload user-inserted images.
(If you want to integrate SM.MS service, use the PowerPack) 
EmojiOne - Open emoji icons
Demo Page and PowerPack/fold-emoji-with-emojione use Emoji icons provided free by EmojiOne (free license)
(You may use other alternatives, eg. twemoji from twitter) 
CodeMirror - In-browser code editor.
RequireJS - A JavaScript AMD module loader.
KaTeX - The fastest math typesetting library for the web.
marked, turndown and more remarkable libs. 
 
 

Contributing

HyperMD is a personal Open-Source project by laobubu. Contributions are welcomed. You may:


Ctrl+Click works too, but will jump to the footnote if exists. ↩2

Math block use $$ to wrap your TeX expression.

Disabled by default, see doc; #use two hash symbol# if tag name contains spaces.

Use Ctrl+Shift+V to paste plain text.

Languages as many as CodeMirror supports.

If the theme is not designed for HyperMD, some features might not be present.


Online Demo | Examples | Documentation

中文介绍


Download Details:

Author: laobubu
Source Code: https://github.com/laobubu/HyperMD 
License: MIT license

#typescript #editor #markdown #codemirror 

HyperMD: A WYSIWYG Markdown Editor for Browsers
Lawrence  Lesch

Lawrence Lesch

1675414260

React-codemirror2: Codemirror integrated Components for React

React-codemirror2

Codemirror integrated Components for React

Install

npm install react-codemirror2 codemirror --save

react-codemirror2 ships with the notion of an uncontrolled and controlled component. UnControlled consists of a simple wrapper largely powered by the inner workings of codemirror itself, while Controlled will demand state management from the user, preventing codemirror changes unless properly handled via value. The latter will offer more control and likely be more appropriate with redux heavy apps.

uncontrolled usage

import {UnControlled as CodeMirror} from 'react-codemirror2'

<CodeMirror
  value='<h1>I ♥ react-codemirror2</h1>'
  options={{
    mode: 'xml',
    theme: 'material',
    lineNumbers: true
  }}
  onChange={(editor, data, value) => {
  }}
/>

controlled usage

import {Controlled as CodeMirror} from 'react-codemirror2'

<CodeMirror
  value={this.state.value}
  options={options}
  onBeforeChange={(editor, data, value) => {
    this.setState({value});
  }}
  onChange={(editor, data, value) => {
  }}
/>

requiring codemirror resources

codemirror comes as a peer dependency, meaning you'll need to require it in your project in addition to react-codemirror2. This prevents any versioning conflicts that would arise if codemirror came as a dependency through this wrapper. It's been observed that version mismatches can cause difficult to trace issues such as syntax highlighting disappearing without any explicit errors/warnings

  • additional

Since codemirror ships mostly unconfigured, the user is left with the responsibility for requiring any additional resources should they be necessary. This is often the case when specifying certain language modes and themes. How to import/require these assets will vary according to the specifics of your development environment. Below is a sample to include the assets necessary to specify a mode of xml (HTML) and a material theme.

note that the base codemirror.css file is required in all use cases

@import 'codemirror/lib/codemirror.css';
@import 'codemirror/theme/material.css';
import CodeMirror from 'react-codemirror2';
require('codemirror/mode/xml/xml');
require('codemirror/mode/javascript/javascript');

props

proptype defaultcomponentsdescription
autoCursorboolean trueControlled UnControlledshould component cursor position correct when value changed
autoScrollboolean trueControlled UnControlledshould component scroll cursor position into view when value changed
classNamestringControlled UnControlledpass through class class="react-codemirror2 className"
defineModeobjectControlled UnControlledpass a custom mode via {name: 'custom', fn: myModeFn}
detachbooleanUnControlledshould component ignore new props
optionsobjectControlled UnControlledcodemirror configuration
valuestring*Controlled UnControlled* component value must be managed for controlled components

props cont. (wrapped codemirror programming api)

will programmatically set cursor to the position specified

<CodeMirror
  [...]
  cursor={{
    line: 5,
    ch: 10
  }}
  onCursor={(editor, data) => {}}
/>

will programmatically scroll to the specified coordinate

<CodeMirror
  [...]
  scroll={{
    x: 50,
    y: 50
  }}
  onScroll={(editor, data) => {}}
/>
  • selection={{ranges: array<{anchor, head}>, focus?: boolean} - setSelections

will programmatically select the ranges specified

<CodeMirror
  [...]
  selection={{
    ranges: [{
      anchor: {ch: 8, line: 5},
      head: {ch: 37, line: 5}
    }],
    focus: true // defaults false if not specified
  }}
  onSelection={(editor, data) => {}}
/>

events

eventcomponentsdescription
editorDidAttach(editor)UnControlledcomponent is now responding to new props
editorDidConfigure(editor)Controlled UnControlledcomponent configuration has been set
editorDidDetach(editor)UnControlledcomponent is now ignoring new props
editorDidMount(editor, [next])Controlled UnControlled* invoking optional next will trigger editorDidConfigure
editorWillUnmount(editor)Controlled UnControlledinvoked before componentWillUnmount
onBeforeChange(editor, data, value, [next])Controlled UnControlled* if used, next is returned via UnControlled and must be invoked to trigger onChange
onChange(editor, data, value)Controlled UnControlledthe component value has been changed

events cont. wrapped codemirror events

FAQ

  • Is server side rendering supported?

Yes. react-codemirror2 will prevent rendering in absence of navigator. You can also force the component to not render via a PREVENT_CODEMIRROR_RENDER global.

  • How can I get the instance?

The recommended technique to get the instance is to persist the editor returned via event callbacks. There is no static method to get it on demand, e.g. CodeMirror.getInstance(). Example...

constructor() {
  this.instance = null;
}

render() {
  <CodeMirror editorDidMount={editor => { this.instance = editor }}/>
}
  • How can I have a resizable editor?

Check out bokuweb/re-resizable. Wrapping your component with <Resizable/>'s works well

Contributing

Pull Requests are welcome. Be mindful of the available scripts below to help submitting a well-received contribution.

  • npm run start to run the app on localhost:8000
  • npm run test to ensure tests continue to pass
  • npm run build to generate the demo bundle

note that it's necessary to bump the package.json version prior to final npm run build so we can grab the proposed new version as seen in the demo header. Also note, the core changes are to be made in src/index.tsx as ./index.js and ./index.d.ts are generated


demo @ scniro.github.io/react-codemirror2


Download Details:

Author: Scniro
Source Code: https://github.com/scniro/react-codemirror2 
License: MIT license

#typescript #react #codemirror 

React-codemirror2: Codemirror integrated Components for React

Comment Créer Un Éditeur De Code Avec Codemirror 6

De nos jours, dans le navigateur, les didacticiels de langage de programmation sont la norme. Ils fournissent tous une sorte d'éditeur de code. Les gens apprennent en tapant du code dans ces éditeurs et un retour instantané leur indique le résultat.

Et si vous vouliez créer une telle plateforme ? Surtout celui qui fonctionne sur un écran tactile et qui est accessible ? Vous voulez créer quelque chose comme Codecademy , Educative ou Codewars ? Je le fais, c'est pourquoi j'ai créé Live JavaScript .

Mais maintenant, il est temps de faire une mise à jour, alors j'ai commencé à voir quel éditeur de code je devrais utiliser là-bas. J'ai essayé plusieurs éditeurs de code, mais un s'est démarqué : CodeMirror . Marijn Haverbeke est en train de réécrire complètement la base de code existante, et le nouveau CodeMirror est un excellent logiciel. J'espère que ce petit guide vous incitera à essayer.

Bien sûr , ce n'est pas un didacticiel exhaustif de CodeMirror 6. Il y a trop de choses qu'il peut faire. C'est peut-être un problème, c'est un peu difficile de trouver ce dont vous avez besoin. Ceci est juste un guide de démarrage rapide.

Installer

CodeMirror 6 est publié sous la forme d'un ensemble de packages NPM sous le @codemirrorchamp d'application. Chaque package expose les modules ECMAScript et CommonJS. Nous devons donc utiliser une sorte de bundler ou de chargeur pour les exécuter dans le navigateur.

Tout d'abord, créez un nouveau dossier pour le projet et initialisez un nouveau projet npm à l'intérieur :

npm init -y

Installez ensuite les packages CodeMirror6 :

npm i @codemirror/basic-setup @codemirror/lang-javascript

Nous pouvons maintenant créer le fichier principal de l'éditeur, editor.js, en ajoutant ce qui suit :

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

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

Créons un styles.css:

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

Enfin, créons un fichier HTML, index.htmlqui utilise le bundle de scripts et le fichier CSS :

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Il editor.bundle.jsmanque pour le moment. Nous devons le générer d'une editor.jsmanière ou d'une autre. Il existe plusieurs façons, en utilisant un outil comme Webpack ou Rollup. Ici, nous utilisons Rollup.

Cumul

Nous devons installer Rollup :

npm i rollup @rollup/plugin-node-resolve

Créons maintenant un fichier de configuration Rollup, appelé rollup.config.js:

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

Pour simplifier les choses, modifiez le package.jsonet ajoutez un "start": "rollup -c"script. package.jsondevrait être quelque chose comme ça :

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

Construire le lot

Maintenant, si tout est correctement configuré, nous pouvons exécuter :

npm start

Et cela va générer le editor.bundle.jsfichier.

Ouvrez index.htmldans un navigateur et vous verrez une seule ligne de l'éditeur :

Si nous regardons le code source, nous pouvons voir que l'éditeur a été ajouté dans le corps :

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

Codemiroir

CodeMirror est une collection de modules qui fournissent un éditeur de texte et de code complet. Cela signifie que vous pouvez choisir les fonctionnalités dont vous avez besoin, mais la configuration d'un éditeur vous oblige à assembler un tas de pièces.

Revenons à la editor.jssource pour voir ce qui s'y passe :

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

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

Il y a deux parties :

  • état (EditorState) : qui contient les structures de données modélisant l'état de l'éditeur (l'état de l'éditeur est composé d'un document en cours et d'une sélection)
  • view (EditorView) : qui fournit l'interface utilisateur de l'éditeur.

L'éditeur sera rendu à l'intérieur du corps. Si vous voulez qu'il soit rendu ailleurs, il vous suffit de changer le sélecteur d'élément défini sur parent.

Langue

Un véritable éditeur de code a besoin de raccourcis clavier, de surbrillance, d'une gouttière de numéro de ligne ou d'un historique d'annulation, etc. Bien que vous puissiez les ajouter manuellement, CodeMirror fournit un package de "configuration de base" qui inclut toutes les fonctionnalités d'un éditeur de code. C'est pourquoi nous utilisons « @codemirror/basic-setup ».

Vous pouvez voir que nous utilisons également le package "@codemirror/lang-javascript". Étant donné que chaque langage de programmation a ses règles et fonctionnalités spécifiques, nous devons utiliser le package de langage approprié. De cette façon, nous bénéficions d'un analyseur, d'une saisie semi-automatique, d'une mise en surbrillance, etc.

Avant de pouvoir utiliser une autre langue, vous devez installer le package correspondant. Voici quelques exemples:

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

Lorsque j'écris ceci, CodeMirror prend en charge CSS, C++, HTML, Java, JavaScript, JSON, Markdown, Python, Rust et XML.

Ensuite, vous importez et utilisez le package, par exemple voici comment utiliser le lang-html:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

Valeur initiale du document

Pour définir une valeur de départ pour l'éditeur, il vous suffit de définir la docpropriété de l'état sur la valeur. Par exemple, si la valeur initiale de l'éditeur est :

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Ensuite, nous le définissons comme docvaleur :

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

Lire et définir le texte du document

Pour lire le texte actuellement affiché par l'éditeur, procédez comme suit :

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

Vous pouvez modifier la valeur de plusieurs manières en répartissant les modifications. NE modifiez PAS directement la doc.

  • Insertion de texte
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • Remplacer du texte
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

Donc, pour remplacer tout ce dont vous avez juste besoin pour obtenir la longueur actuelle du texte :

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

Vous pouvez envoyer plusieurs modifications à la fois si vous en avez besoin, changesil doit s'agir d'un tableau de modifications individuelles.

Écoutez les changements

Habituellement, vous voulez écouter les changements et faire quelque chose avec le nouveau code, par exemple, pour réinterpréter/évaluer le code. Cela se fait en ajoutant un updateListenerà la vue de l'éditeur comme ceci :

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

Bien sûr, si vous ne voulez pas faire beaucoup de traitement à chaque fois qu'une touche est enfoncée, vous devez utiliser un délai d'attente :

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

Thèmes

Les thèmes, comme tout le reste, peuvent être fournis sous forme de packages NPM. Ou vous pouvez fournir votre thème. Pour le moment (CodeMirror6 est encore en beta) il n'y a qu'un seul thème sombre que vous pouvez installer, One Dark:

npm i @codemirror/theme-one-dark

Vous pouvez utiliser un thème comme n'importe quoi d'autre : vous l'importez et l'utilisez comme extension :

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

Comme je l'ai dit, vous pouvez utiliser votre thème. CodeMirror 6 utilise CSS-in-JS.

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})

Si vous ne souhaitez pas fournir de styles pour tout, vous pouvez étendre le thème de base et n'en écraser que certaines parties. Voici un exemple qui modifie la famille et la taille de la police de l'éditeur :

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

Tout mettre ensemble

La seule pièce manquante est un évaluateur de code, qui pour JavaScript est facile à faire (puisqu'il s'exécute dans le navigateur). Ajoutez-en quelques-uns console.log()pour voir les résultats de l'exécution du code sur la console DevTools du navigateur.

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

Et c'est tout. Vous avez un joli terrain de jeu JavaScript qui peut être intégré dans une plateforme plus grande.

 Source : https://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

   #codemirror  #code #javascript 

Comment Créer Un Éditeur De Code Avec Codemirror 6
高橋  花子

高橋 花子

1658234520

Codemirror6を使用してコードエディタを作成する

最近の実践的なブラウザでは、プログラミング言語のチュートリアルが標準となっています。それらはすべて、ある種のコードエディタを提供します。人々はそれらのエディターにいくつかのコードを入力することで学び、即座のフィードバックが結果を伝えます。

そのようなプラットフォームを作成したい場合はどうなりますか?特にタッチスクリーンで動作し、アクセス可能なものはありますか?CodecademyEducativeCodewarsなどを作成したいですか?そうすることで、LiveJavaScriptを作成しまし

しかし、今はアップグレードの時間なので、そこで使用するコードエディタを確認し始めました。いくつかのコードエディタを試しましたが、目立ったのはCodeMirrorです。Marijn Haverbekeは、既存のコードベースを完全に書き直しており、新しいCodeMirrorは優れたソフトウェアです。この小さなガイドがあなたにそれを試してみるように勧めてくれることを願っています。

とにかく、これはCodeMirror6の完全なチュートリアルではありません。実行できることが多すぎます。多分それは問題です、あなたが必要とするものを見つけるのは少し難しいです。これは、簡単なスタートガイドです。

設定

CodeMirror 6は、@codemirrorスコープの下でNPMパッケージのセットとして公開されます。各パッケージは、ECMAScriptおよびCommonJSモジュールを公開します。したがって、ブラウザで実行するには、ある種のバンドラーまたはローダーを使用する必要があります。

まず、プロジェクト用の新しいフォルダーを作成し、その中に新しいnpmプロジェクトを初期化します。

npm init -y

次に、CodeMirror6パッケージをインストールします。

npm i @codemirror/basic-setup @codemirror/lang-javascript

これで、メインエディタファイルを作成してeditor.js、以下を追加できます。

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

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

を作成しましょうstyles.css

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

index.html最後に、スクリプトバンドルとCSSファイルを使用するHTMLファイルを作成しましょう。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

今のeditor.bundle.jsところ行方不明です。editor.jsなんとかして生成する必要があります。WebpackやRollupなどのツールを使用する方法はいくつかあります。ここではロールアップを使用します。

巻き上げる

Rollupをインストールする必要があります。

npm i rollup @rollup/plugin-node-resolve

次に、次の名前のロールアップ構成ファイルを作成しましょうrollup.config.js

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

簡単にするには、を編集しpackage.jsonてスクリプトを追加し"start": "rollup -c"ます。package.json次のようになります。

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

バンドルを構築する

これで、すべてが正しく設定されていれば、次のように実行できます。

npm start

そして、これはeditor.bundle.jsファイルを生成します。

ブラウザで開くindex.htmlと、エディタが1行表示されます。

ソースコードを見ると、エディタが本文に追加されていることがわかります。

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

コードミラー

CodeMirrorは、フル機能のテキストおよびコードエディタを提供するモジュールのコレクションです。つまり、必要な機能を選択できますが、エディターを設定するには、多数の要素をまとめる必要があります。

editor.jsソースに戻って、そこで何が起こっているかを見てみましょう。

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

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

2つの部分があります:

  • state(EditorState):エディターの状態をモデル化するデータ構造が含まれます(エディターの状態は、現在のドキュメントと選択範囲で構成されます)
  • ビュー(EditorView):エディターのUIを提供します。

エディターはボディ内にレンダリングされます。他の場所でレンダリングしたい場合は、要素セレクターセットをに変更する必要がありますparent

言語

実際のコードエディタには、キーバインディング、強調表示、行番号ガター、元に戻す履歴などが必要です。それぞれを手動で追加できますが、CodeMirrorは、コードエディタのすべての機能を含む「基本設定」パッケージを提供します。これが、「@ codemirror/basic-setup」を使用する理由です。

「@codemirror/lang-javascript」パッケージも使用していることがわかります。各プログラミング言語には固有のルールと機能があるため、正しい言語パッケージを使用する必要があります。このようにして、パーサー、オートコンプリート、ハイライトなどの恩恵を受けます。

別の言語を使用する前に、対応するパッケージをインストールする必要があります。ここではいくつかの例を示します。

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

私が書いているとき、このCodeMirrorはCSS、C ++、HTML、Java、JavaScript、JSON、Markdown、Python、Rust、およびXMLをサポートしています。

次に、パッケージをインポートして使用します。たとえば、次のように使用しますlang-html

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

ドキュメントの初期値

docエディターの開始値を設定するには、状態のプロパティを値に設定する必要があります。たとえば、エディタの初期値が次の場合:

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

次に、それをdoc値として設定します。

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

ドキュメントのテキストの読み取りと設定

エディターによって表示される現在のテキストを読み取るには、次のようにします。

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

変更をディスパッチすることにより、複数の方法で値を変更できます。ドキュメントを直接変更しないでください。

  • テキストの挿入
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • テキストの置き換え
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

したがって、すべてを置き換えるには、現在のテキストの長さを取得する必要があります。

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

必要に応じて、一度に複数の変更をディスパッチできますchanges。これは、個々の変更の配列である必要があります。

変更を聞く

通常、変更をリッスンし、新しいコードで何かを実行して、たとえば、コードを再解釈/評価する必要があります。updateListenerこれは、次のようにエディタビューにを追加することによって行われます。

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

もちろん、キーが押されるたびに多くの処理を実行したくない場合は、そこでタイムアウトを使用する必要があります。

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

テーマ

テーマは、他のものと同様に、NPMパッケージとして提供できます。または、テーマを提供することもできます。現時点では(CodeMirror6はまだベータ版です)、インストールできるダークテーマは1つだけですOne Dark

npm i @codemirror/theme-one-dark

テーマは他のものと同じように使用できます。インポートして拡張機能として使用します。

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

私が言ったように、あなたはあなたのテーマを使うことができます。CodeMirror6はCSS-in-JSを使用します。

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})

すべてにスタイルを提供したくない場合は、基本テーマを拡張して、その一部のみを上書きできます。エディターのフォントファミリーとサイズを変更する例を次に示します。

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

すべてをまとめる

唯一欠けているのはコードエバリュエーターです。これはJavaScriptの場合は簡単に実行できます(これはブラウザーで実行されるため)。いくつか追加するだけconsole.log()で、ブラウザーのDevToolsコンソールでコード実行の結果を確認できます。

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

そしてそれがすべてです。より大きなプラットフォームに統合できる素晴らしいJavaScriptプレイグラウンドがあります。

 ソース:https ://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

 #codemirror  #code #javascript 

Codemirror6を使用してコードエディタを作成する

Como Fazer Um Editor De Código Com O Codemirror 6

Hoje em dia, no navegador, os tutoriais de linguagem de programação são o padrão. Todos eles fornecem algum tipo de editor de código. As pessoas aprendem digitando algum código nesses editores e o feedback instantâneo informa o resultado.

E se você quiser criar uma plataforma assim? Especialmente um que funcione em uma tela sensível ao toque e que seja acessível? Você quer criar algo como Codecademy , Educative ou Codewars ? Eu faço isso é por isso que eu criei o Live JavaScript .

Mas agora é hora de um upgrade, então comecei a ver qual editor de código eu deveria usar lá. Tentei vários editores de código, mas um se destacou: CodeMirror . Marijn Haverbeke está reescrevendo completamente a base de código existente, e o novo CodeMirror é um ótimo software. Espero que este pequeno guia o encoraje a experimentá-lo.

De qualquer forma, este não é um tutorial exaustivo do CodeMirror 6. Há muitas coisas que ele pode fazer. Talvez isso seja um problema, é um pouco difícil encontrar o que você precisa. Este é apenas um guia rápido de introdução.

Configurar

O CodeMirror 6 é publicado como um conjunto de pacotes NPM sob o @codemirrorescopo. Cada pacote expõe módulos ECMAScript e CommonJS. Portanto, precisamos usar algum tipo de bundler ou loader para executá-los no navegador.

Primeiro, crie uma nova pasta para o projeto e inicialize um novo projeto npm dentro:

npm init -y

Em seguida, instale os pacotes do CodeMirror6:

npm i @codemirror/basic-setup @codemirror/lang-javascript

Agora podemos criar o arquivo do editor principal, editor.js, adicionando o seguinte:

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

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

Vamos criar um styles.css:

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

Finalmente vamos criar um arquivo HTML, index.htmlque usa o pacote de scripts e o arquivo CSS:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

O editor.bundle.jsestá em falta no momento. Precisamos gerá-lo de editor.jsalguma forma. Existem várias maneiras, usando uma ferramenta como Webpack ou Rollup. Aqui usamos Rollup.

Rolar

Precisamos instalar o Rollup:

npm i rollup @rollup/plugin-node-resolve

Agora vamos criar um arquivo de configuração Rollup, chamado rollup.config.js:

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

Para simplificar as coisas, edite o arquivo package.jsone adicione um "start": "rollup -c"script. package.jsondeve ser algo assim:

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

Construir o pacote

Agora, se tudo estiver configurado corretamente, podemos executar:

npm start

E isso irá gerar o editor.bundle.jsarquivo.

Abra index.htmlem um navegador e você verá uma única linha do editor:

Se observarmos o código-fonte, podemos ver que o editor foi adicionado no corpo:

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

Codemirror

CodeMirror é uma coleção de módulos que fornecem um texto completo e um editor de código. Isso significa que você pode escolher quais recursos você precisa, mas configurar um editor exige que você monte um monte de peças.

Vamos voltar à editor.jsfonte para ver o que está acontecendo lá:

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

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

Existem duas partes:

  • state (EditorState): que contém as estruturas de dados que modelam o estado do editor (o estado do editor é composto por um documento atual e uma seleção)
  • view (EditorView): que fornece a interface do usuário do editor.

O editor será renderizado dentro do corpo. Se você quiser renderizar em outro lugar, basta alterar o seletor de elemento definido para parent.

Linguagem

Um editor de código real precisa de atalhos de teclado, realce, uma medianiz de número de linha ou um histórico de desfazer, etc. Embora você possa adicionar cada um manualmente, o CodeMirror fornece um pacote de “configuração básica” que inclui todas as funcionalidades de um editor de código. É por isso que usamos “@codemirror/basic-setup”.

Você pode ver que também usamos o pacote “@codemirror/lang-javascript”. Como cada linguagem de programação tem suas regras e recursos específicos, precisamos usar o pacote de linguagem correto. Dessa forma, nos beneficiamos de um analisador, preenchimento automático, realce e assim por diante.

Antes de poder usar um idioma diferente, você precisa instalar o pacote correspondente. aqui estão alguns exemplos:

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

Quando estou escrevendo este CodeMirror suporta CSS, C++, HTML, Java, JavaScript, JSON, Markdown, Python, Rust e XML.

Então você importa e usa o pacote, por exemplo, aqui está como usar o lang-html:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

Valor inicial do documento

Para definir um valor inicial para o editor, basta definir a docpropriedade do estado para o valor. Por exemplo, se o valor inicial do editor for:

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Então definimos como docvalor:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

Lendo e configurando o texto do documento

Para ler o texto atual exibido pelo editor você faz:

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

Você pode alterar o valor de várias maneiras enviando as alterações. NÃO altere o documento diretamente.

  • Inserindo texto
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • Substituindo o texto
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

Então, para substituir tudo, você só precisa obter o comprimento do texto atual:

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

Você pode despachar várias alterações de uma só vez, se precisar, changesdeve ser uma matriz de alterações individuais.

Ouça as mudanças

Normalmente, você quer ouvir as mudanças e fazer algo com o novo código, por exemplo, para reinterpretar/avaliar o código. Isso é feito adicionando um updateListenerà visualização do editor assim:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

Claro, se você não quiser fazer muito processamento cada vez que uma tecla é pressionada, você precisa usar um tempo limite:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

Temas

Os temas, como qualquer outra coisa, podem ser fornecidos como pacotes NPM. Ou você pode fornecer seu tema. No momento (CodeMirror6 ainda está em beta) há apenas um tema escuro que você pode instalar One Dark:

npm i @codemirror/theme-one-dark

Você pode usar um tema como qualquer outra coisa: você o importa e o usa como uma extensão:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

Como eu disse, você pode usar o seu tema. CodeMirror 6 usa CSS-in-JS.

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})

Se você não quiser fornecer estilos para tudo, poderá estender o tema base e sobrescrever apenas partes dele. Aqui está um exemplo que altera a família e o tamanho da fonte do editor:

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

Juntando tudo

A única peça que falta é um avaliador de código, que para JavaScript é fácil de fazer (já que roda no navegador). Basta adicionar alguns console.log()para ver os resultados da execução do código no console do DevTools do navegador.

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

E isso é tudo. Você tem um bom playground JavaScript que pode ser integrado a uma plataforma maior.

 Fonte: https://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

   #codemirror  #code #javascript 

Como Fazer Um Editor De Código Com O Codemirror 6
曾 俊

曾 俊

1658233980

如何使用 Codemirror 6 制作代码编辑器

如今,在浏览器中动手操作,编程语言教程是标准。它们都提供了某种代码编辑器。人们通过在这些编辑器中输入一些代码来学习,即时反馈会告诉他们结果。

如果你想创建这样一个平台怎么办?尤其是在触摸屏上工作并且可以访问的那个?您想创建类似CodecademyEducativeCodewars的东西吗?我这样做了,这就是我创建Live JavaScript的原因。

但是现在是升级的时候了,所以我开始看看我应该在那里使用什么代码编辑器。我尝试了几种代码编辑器,但其中一个脱颖而出:CodeMirror。Marijn Haverbeke 正在对现有代码库进行完全重写,而新的 CodeMirror 是一款很棒的软件。我希望这个小指南能鼓励你尝试一下。

无论如何,这不是CodeMirror 6 的详尽教程。它可以做的事情太多了。也许这是一个问题,有点难以找到你需要的东西。这只是一个快速入门指南。

设置

CodeMirror 6 作为@codemirror范围内的一组 NPM 包发布。每个包都公开了 ECMAScript 和 CommonJS 模块。所以我们需要使用某种捆绑器或加载器在浏览器中运行它们。

首先,为项目创建一个新文件夹,并在里面初始化一个新的 npm 项目:

npm init -y

然后安装 CodeMirror6 包:

npm i @codemirror/basic-setup @codemirror/lang-javascript

现在我们可以创建主编辑器文件editor.js,添加以下内容:

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

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

让我们创建一个styles.css

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

最后让我们创建一个 HTML 文件,index.html它使用脚本包和 CSS 文件:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

editor.bundle.js暂时不见了。我们需要以editor.js某种方式生成它。有几种方法,使用 Webpack 或 Rollup 之类的工具。这里我们使用 Rollup。

卷起

我们需要安装 Rollup:

npm i rollup @rollup/plugin-node-resolve

现在让我们创建一个汇总配置文件,名为rollup.config.js

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

为了简单起见,编辑package.json并添加"start": "rollup -c"脚本。package.json应该是这样的:

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

构建捆绑包

现在,如果一切设置正确,我们可以运行:

npm start

这将生成editor.bundle.js文件。

在浏览器中打开index.html,您将看到编辑器的一行:

如果我们查看源代码,我们可以看到在正文中添加了编辑器:

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

代码镜像

CodeMirror 是一组提供全功能文本和代码编辑器的模块。这意味着您可以选择所需的功能,但设置编辑器需要您将一堆部分放在一起。

让我们回到editor.js源头,看看那里发生了什么:

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

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

有两个部分:

  • 状态(EditorState):其中包含对编辑器状态建模的数据结构(编辑器状态由当前文档和选择组成)
  • 视图(EditorView):提供编辑器的UI。

编辑器将在正文中呈现。如果您希望它在其他地方呈现,您只需将元素选择器设置为parent.

真正的代码编辑器需要键绑定、突出显示、行号间距或撤消历史记录等。虽然您可以手动添加每个,但 CodeMirror 提供了一个“基本设置”包,其中包含代码编辑器的所有功能。这就是我们使用“@codemirror/basic-setup”的原因。

你可以看到我们也使用了“@codemirror/lang-javascript”包。由于每种编程语言都有其特定的规则和特性,因此我们需要使用正确的语言包。通过这种方式,我们受益于解析器、自动完成、突出显示等。

在您可以使用不同的语言之前,您需要安装相应的软件包。这里有些例子:

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

当我编写此 CodeMirror 时,它支持 CSS、C++、HTML、Java、JavaScript、JSON、Markdown、Python、Rust 和 XML。

然后你导入并使用包,例如这里是如何使用lang-html

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

文档初始值

要为编辑器设置起始值,您只需将状态的doc属性设置为该值。例如,如果初始编辑器值为:

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

然后我们将其设置为doc值:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

阅读和设置文档的文本

要阅读编辑器显示的当前文本,请执行以下操作:

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

您可以通过调度更改以多种方式更改值。不要直接更改文档。

  • 插入文本
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • 替换文本
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

因此,要替换您只需要获取当前文本长度的所有内容:

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

如果需要,您可以一次分派多个更改,changes必须是一组单独的更改。

聆听变化

通常,您想监听变化并对新代码做一些事情,例如,重新解释/评估代码。这是通过updateListener像这样向编辑器视图添加 a 来完成的:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

当然,如果您不想在每次按下键时进行大量处理,则需要在此处使用超时:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

主题

与其他任何东西一样,主题可以作为 NPM 包提供。或者您可以提供您的主题。目前(CodeMirror6 仍处于测试阶段)您只能安装一个深色主题One Dark

npm i @codemirror/theme-one-dark

您可以像使用其他任何主题一样使用主题:导入它并将其用作扩展:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

正如我所说,您可以使用您的主题。CodeMirror 6 使用 CSS-in-JS。

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})

如果您不想为所有内容提供样式,则可以扩展基本主题并仅覆盖其中的一部分。这是一个更改编辑器字体系列和大小的示例:

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

放在一起

唯一缺少的部分是代码评估器,这对于 JavaScript 来说很容易做到(因为它在浏览器中运行)。只需添加一些console.log()即可在浏览器的 DevTools 控制台上查看代码执行的结果。

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

仅此而已。你有一个很好的 JavaScript 游乐场,可以集成到更大的平台中。

 来源:https ://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

   #codemirror  #code #javascript 

如何使用 Codemirror 6 制作代码编辑器

Cómo Hacer Un Editor De Código Con Codemirror 6

Estos días prácticos, en el navegador, los tutoriales de lenguaje de programación son el estándar. Todos ellos proporcionan algún tipo de editor de código. Las personas aprenden escribiendo algún código en esos editores y la retroalimentación instantánea les dice el resultado.

¿Qué pasa si quieres crear una plataforma de este tipo? ¿Especialmente uno que funcione en una pantalla táctil y que sea accesible? ¿Quieres crear algo como Codecademy , Educative o Codewars ? Lo hago, por eso creé Live JavaScript .

Pero ahora es el momento de una actualización, así que comencé a ver qué editor de código debería usar allí. Probé varios editores de código, pero uno se destacó: CodeMirror . Marijn Haverbeke está reescribiendo por completo el código base existente, y el nuevo CodeMirror es una gran pieza de software. Espero que esta pequeña guía te anime a darle una oportunidad.

Por supuesto, este no es un tutorial exhaustivo de CodeMirror 6. Hay demasiadas cosas que puede hacer. Tal vez eso sea un problema, es un poco difícil encontrar lo que necesitas. Esta es solo una guía rápida de inicio.

Configuración

CodeMirror 6 se publica como un conjunto de paquetes NPM bajo el @codemirroralcance. Cada paquete expone módulos ECMAScript y CommonJS. Por lo tanto, debemos usar algún tipo de paquete o cargador para ejecutarlos en el navegador.

Primero, cree una nueva carpeta para el proyecto e inicialice un nuevo proyecto npm dentro:

npm init -y

Luego instale los paquetes de CodeMirror6:

npm i @codemirror/basic-setup @codemirror/lang-javascript

Ahora podemos crear el archivo del editor principal editor.js, agregando lo siguiente:

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

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

Vamos a crear un styles.css:

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

Finalmente, creemos un archivo HTML, index.htmlque usa el paquete de secuencias de comandos y el archivo CSS:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

editor.bundle.jsFalta el por el momento . Necesitamos generarlo de editor.jsalguna manera. Hay varias formas, usando una herramienta como Webpack o Rollup. Aquí usamos Rollup.

Enrollar

Necesitamos instalar Rollup:

npm i rollup @rollup/plugin-node-resolve

Ahora vamos a crear un archivo de configuración de resumen, llamado rollup.config.js:

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

Para simplificar las cosas, edite package.jsony agregue un "start": "rollup -c"script. package.jsondebería ser algo como esto:

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

Construye el paquete

Ahora, si todo está configurado correctamente, podemos ejecutar:

npm start

Y esto generará el editor.bundle.jsarchivo.

Abra index.htmlen un navegador y verá una sola línea del editor:

Si miramos el código fuente, podemos ver que el editor fue agregado en el cuerpo:

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

codigoespejo

CodeMirror es una colección de módulos que proporciona un editor de código y texto con todas las funciones. Esto significa que puede elegir qué funciones necesita, pero configurar un editor requiere que junte un montón de piezas.

Volvamos a la editor.jsfuente para ver qué está pasando allí:

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

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

Hay dos partes:

  • state (EditorState): que contiene las estructuras de datos que modelan el estado del editor (el estado del editor se compone de un documento actual y una selección)
  • vista (EditorView): que proporciona la interfaz de usuario del editor.

El editor se renderizará dentro del cuerpo. Si desea que se represente en otro lugar, solo necesita cambiar el conjunto de selector de elementos a parent.

Idioma

Un editor de código real necesita enlaces de teclas, resaltado, un canal de número de línea o un historial de deshacer, etc. Si bien puede agregar cada uno manualmente, CodeMirror proporciona un paquete de "configuración básica" que incluye toda la funcionalidad de un editor de código. Es por eso que usamos "@codemirror/basic-setup".

Puede ver que también usamos el paquete “@codemirror/lang-javascript”. Dado que cada lenguaje de programación tiene sus reglas y características específicas, debemos usar el paquete de lenguaje correcto. De esta manera nos beneficiamos de un analizador, autocompletado, resaltado, etc.

Antes de que pueda usar un idioma diferente, debe instalar el paquete correspondiente. Aquí hay unos ejemplos:

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

Cuando escribo esto, CodeMirror es compatible con CSS, C++, HTML, Java, JavaScript, JSON, Markdown, Python, Rust y XML.

Luego, importa y usa el paquete, por ejemplo, aquí se explica cómo usar lang-html:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

Valor inicial del documento

Para establecer un valor de inicio para el editor, solo necesita establecer la docpropiedad del estado en el valor. Por ejemplo, si el valor inicial del editor es:

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Luego lo establecemos como docvalor:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

Leer y configurar el texto del documento

Para leer el texto actual que muestra el editor, haga lo siguiente:

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

Puede cambiar el valor de varias formas mediante el envío de cambios. NO cambie el documento directamente.

  • Insertar texto
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • Reemplazo de texto
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

Entonces, para reemplazar todo lo que necesita para obtener la longitud del texto actual:

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

Puede enviar varios cambios a la vez si lo necesita, changesdebe ser una matriz de cambios individuales.

Escuche los cambios

Por lo general, desea escuchar el cambio y hacer algo con el nuevo código, por ejemplo, para reinterpretar/evaluar el código. Esto se hace agregando una updateListenera la vista del editor como esta:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

Por supuesto, si no desea hacer mucho procesamiento cada vez que se presiona una tecla, debe usar un tiempo de espera allí:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

Temas

Los temas, como cualquier otra cosa, se pueden proporcionar como paquetes de NPM. O puede proporcionar su tema. Por el momento (CodeMirror6 todavía está en versión beta) solo hay un tema oscuro que puede instalar One Dark:

npm i @codemirror/theme-one-dark

Puedes usar un tema como cualquier otra cosa: lo importas y lo usas como una extensión:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

Como he dicho, puedes usar tu tema. CodeMirror 6 usa CSS-in-JS.

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})

Si no desea proporcionar estilos para todo, puede ampliar el tema base y sobrescribir solo partes del mismo. Aquí hay un ejemplo que cambia la familia de fuentes y el tamaño del editor:

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

Poniendo todo junto

La única pieza que falta es un evaluador de código, que para JavaScript es fácil de hacer (ya que se ejecuta en el navegador). Simplemente agregue algunos console.log()para ver los resultados de la ejecución del código en la consola DevTools del navegador.

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

Y eso es todo. Tienes un buen patio de juegos de JavaScript que se puede integrar en una plataforma más grande.

 Fuente: https://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

   #codemirror  #code #javascript 

Cómo Hacer Un Editor De Código Con Codemirror 6
Duong Tran

Duong Tran

1658230200

Cách Tạo Một Trình Soạn Thảo Mã Với Codemirror 6

Ngày nay, thực hành trên trình duyệt, các hướng dẫn về ngôn ngữ lập trình là tiêu chuẩn. Tất cả đều cung cấp một số loại trình soạn thảo mã. Mọi người học bằng cách gõ một số mã vào các trình soạn thảo đó và phản hồi tức thì cho họ biết kết quả.

Nếu bạn muốn tạo một nền tảng như vậy thì sao? Đặc biệt là một trong những hoạt động trên màn hình cảm ứng và có thể truy cập được? Bạn muốn tạo thứ gì đó như Codecademy , Educative hoặc Codewars ? Tôi làm như vậy, đó là lý do tại sao tôi tạo Live JavaScript .

Nhưng bây giờ đã đến lúc nâng cấp, vì vậy tôi bắt đầu xem tôi nên sử dụng trình soạn thảo mã nào ở đó. Tôi đã thử một số trình chỉnh sửa mã, nhưng một trình soạn thảo nổi bật: CodeMirror . Marijn Haverbeke đang viết lại hoàn toàn cơ sở mã hiện có và CodeMirror mới là một phần mềm tuyệt vời. Tôi hy vọng hướng dẫn nhỏ này sẽ khuyến khích bạn thử.

Nói cách khác , đây không phải là một hướng dẫn đầy đủ về CodeMirror 6. Có quá nhiều thứ nó có thể làm. Có thể đó là một vấn đề, là một chút khó khăn để tìm thấy những gì bạn cần. Đây chỉ là một hướng dẫn bắt đầu nhanh.

Thành lập

CodeMirror 6 được xuất bản dưới dạng một tập hợp các gói NPM trong @codemirrorphạm vi. Mỗi gói hiển thị các mô-đun ECMAScript và CommonJS. Vì vậy, chúng tôi cần sử dụng một số loại gói hoặc trình tải để chạy chúng trong trình duyệt.

Đầu tiên, tạo một thư mục mới cho dự án và khởi tạo một dự án npm mới bên trong:

npm init -y

Sau đó cài đặt các gói CodeMirror6:

npm i @codemirror/basic-setup @codemirror/lang-javascript

Bây giờ chúng ta có thể tạo tệp trình chỉnh sửa chính editor.js, thêm những thứ sau:

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

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

Hãy tạo một styles.css:

html, 
body {
    margin: 0;
    padding: 0;
    background: #fff;
    color: #444;
    font-family: 'Gill Sans','Gill Sans MT',Calibri,'Trebuchet MS',sans-serif
}

Cuối cùng, hãy tạo một tệp HTML, index.htmlsử dụng gói tập lệnh và tệp CSS:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CodeMirror6</title>
  <link rel=stylesheet href="styles.css">
</head>
<body>
    <main>
        <h1>CodeMirror 6</h1>
        <div id="editor">
            <!-- code editor goes here  -->
        </div>
    </main>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Hiện editor.bundle.jsđang mất tích. Chúng ta cần tạo ra nó từ một editor.jscách nào đó. Có một số cách, sử dụng một công cụ như Webpack hoặc Rollup. Ở đây chúng tôi sử dụng Rollup.

Tổng hợp

Chúng tôi cần cài đặt Rollup:

npm i rollup @rollup/plugin-node-resolve

Bây giờ, hãy tạo một tệp cấu hình Rollup, được gọi là rollup.config.js:

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

Để làm cho mọi thứ trở nên đơn giản, hãy chỉnh sửa package.jsonvà thêm một "start": "rollup -c"tập lệnh. package.jsonphải là một cái gì đó như thế này:

{
  "name": "codemirror6",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "rollup -c"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@codemirror/basic-setup": "^0.18.2",
    "@codemirror/lang-javascript": "^0.18.0",
    "@rollup/plugin-node-resolve": "^13.0.0",
    "rollup": "^2.50.6"
  }
}

Xây dựng gói

Bây giờ, nếu mọi thứ được thiết lập chính xác, chúng tôi có thể chạy:

npm start

Và điều này sẽ tạo ra editor.bundle.jstệp.

Mở index.htmltrong trình duyệt và bạn sẽ thấy một dòng duy nhất của trình chỉnh sửa:

Nếu chúng ta nhìn vào mã nguồn, chúng ta có thể thấy rằng trình chỉnh sửa đã được thêm vào phần nội dung:

<div class="cm-editor cm-wrap ͼ1 ͼ2 ͼ4">
    <div aria-live="polite" style="position: absolute; top: -10000px;"></div>
    <div tabindex="-1" class="cm-scroller">
        <div class="cm-gutters" aria-hidden="true" style="position: sticky; min-height: 26px;">
            <div class="cm-gutter cm-lineNumbers">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">9</div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;">1</div>
            </div>
            <div class="cm-gutter cm-foldGutter">
                <div class="cm-gutterElement" style="height: 0px; visibility: hidden; pointer-events: none;">
                    <span title="Unfold line">›</span>
                </div>
                <div class="cm-gutterElement cm-activeLineGutter" style="height: 18px; margin-top: 4px;"></div>
            </div>
        </div>
        <div spellcheck="false" autocorrect="off" autocapitalize="off" contenteditable="true" class="cm-content" style="tab-size: 4" role="textbox" aria-multiline="true" aria-autocomplete="list">
            <div class="cm-activeLine cm-line"><br></div>
        </div>
        <div class="cm-selectionLayer" aria-hidden="true"></div>
        <div class="cm-cursorLayer" aria-hidden="true" style="animation-duration: 1200ms;">
            <div class="cm-cursor cm-cursor-primary" style="left: 34.8125px; top: 5px; height: 15px;"></div>
        </div>
    </div>
</div>

Codemirror

CodeMirror là một tập hợp các mô-đun cung cấp trình soạn thảo văn bản và mã đầy đủ tính năng. Điều này có nghĩa là bạn có thể chọn và chọn các tính năng bạn cần, nhưng việc thiết lập một trình chỉnh sửa yêu cầu bạn phải tập hợp nhiều phần lại với nhau.

Hãy quay lại editor.jsnguồn để xem điều gì đang xảy ra ở đó:

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

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

Có hai phần:

  • trạng thái (EditorState): chứa các cấu trúc dữ liệu mô hình trạng thái trình soạn thảo (trạng thái trình soạn thảo được tạo thành từ một tài liệu hiện tại và một lựa chọn)
  • chế độ xem (EditorView): cung cấp giao diện người dùng của trình soạn thảo.

Trình chỉnh sửa sẽ được hiển thị bên trong nội dung. Nếu bạn muốn nó hiển thị ở một nơi khác, bạn chỉ cần thay đổi bộ chọn phần tử được đặt thành parent.

Ngôn ngữ

Một trình chỉnh sửa mã thực sự cần các liên kết chính, đánh dấu, rãnh số dòng hoặc lịch sử hoàn tác, v.v. Mặc dù bạn có thể thêm từng thứ theo cách thủ công, CodeMirror cung cấp gói “thiết lập cơ bản” bao gồm tất cả các chức năng của trình chỉnh sửa mã. Đây là lý do tại sao chúng tôi sử dụng “@ codemirror / basic-setup”.

Bạn có thể thấy rằng chúng tôi cũng sử dụng gói “@ codemirror / lang-javascript”. Vì mỗi ngôn ngữ lập trình có các quy tắc và tính năng cụ thể của nó, chúng ta cần sử dụng gói ngôn ngữ chính xác. Bằng cách này, chúng tôi được hưởng lợi từ trình phân tích cú pháp, tự động hoàn thành, đánh dấu, v.v.

Trước khi có thể sử dụng một ngôn ngữ khác, bạn cần cài đặt gói tương ứng. Dưới đây là một số ví dụ:

npm i @codemirror/lang-html
npm i @codemirror/lang-css
npm i @codemirror/lang-json

Khi tôi viết CodeMirror này hỗ trợ CSS, C ++, HTML, Java, JavaScript, JSON, Markdown, Python, Rust và XML.

Sau đó, bạn nhập và sử dụng gói, ví dụ đây là cách sử dụng lang-html:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()]
  }),
  parent: document.body
})

Giá trị ban đầu của tài liệu

Để đặt giá trị bắt đầu cho trình chỉnh sửa, bạn chỉ cần đặt thuộc tính của trạng thái docthành giá trị. Ví dụ: nếu giá trị trình soạn thảo ban đầu là:

<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>

Sau đó, chúng tôi đặt nó là docgiá trị:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
  state: EditorState.create({
    extensions: [basicSetup, html()],
    doc:`<html lang="en">
<body>
    <h1>CodeMirror 6</h1>
    <script src="editor.bundle.js"></script>  
</body>
</html>`
  }),
  parent: document.body
})

Đọc và thiết lập văn bản của tài liệu

Để đọc văn bản hiện tại được hiển thị bởi trình soạn thảo, bạn thực hiện:

const currentValue = editor.state.doc.toString();

// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>

Bạn có thể thay đổi giá trị theo nhiều cách bằng cách thực hiện các thay đổi. KHÔNG thay đổi tài liệu trực tiếp.

  • Chèn văn bản
editor.dispatch({
  changes: {from: 0, insert: "<!DOCTYPE html>\n"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="editor.bundle.js"></script>  
// </body>
// </html>
  • Thay thế văn bản
const currentValue = editor.state.doc.toString();
const startPosition = currentValue.indexOf("editor.bundle.js");
const endPosition = startPosition + "editor.bundle.js".length;

editor.dispatch({
  changes: {
    from: startPosition, 
    to: endPosition,
    insert: "code-mirror-editor.js"}
})

// <!DOCTYPE html>
// <html lang="en">
// <body>
//    <h1>CodeMirror 6</h1>
//    <script src="code-mirror-editor.js"></script>  
// </body>
// </html>

Vì vậy, để thay thế mọi thứ, bạn chỉ cần lấy độ dài văn bản hiện tại:

const currentValue = editor.state.doc.toString();
const endPosition = currentValue.length;

editor.dispatch({
  changes: {
    from: 0, 
    to: endPosition,
    insert: "something else"}
})

Bạn có thể gửi nhiều thay đổi cùng một lúc nếu bạn cần, changesphải là một loạt các thay đổi riêng lẻ.

Lắng nghe các thay đổi

Thông thường, bạn muốn lắng nghe sự thay đổi và làm điều gì đó với mã mới, chẳng hạn như để diễn giải lại / đánh giá mã. Điều này được thực hiện bằng cách thêm một updateListenervào dạng xem trình chỉnh sửa như sau:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let editor = new EditorView({
 state: EditorState.create({
   extensions: [
     basicSetup, 
     html(),
     EditorView.updateListener.of((v)=> {
     if(v.docChanged) {
         console.log('DO SOMETHING WITH THE NEW CODE');
     }
   })]],    
 }),
 parent: document.body
})

Tất nhiên, nếu bạn không muốn thực hiện nhiều thao tác xử lý mỗi lần nhấn một phím, bạn cần sử dụng thời gian chờ ở đó:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            console.log('DO SOMETHING WITH THE NEW CODE');
          }, 500 );
        }
      })
    ],    
  }),
  parent: document.body
})

Chủ đề

Chủ đề, giống như bất kỳ thứ gì khác, có thể được cung cấp dưới dạng gói NPM. Hoặc bạn có thể cung cấp chủ đề của mình. Hiện tại (CodeMirror6 vẫn đang trong giai đoạn thử nghiệm), chỉ có một chủ đề tối mà bạn có thể cài đặt One Dark,:

npm i @codemirror/theme-one-dark

Bạn có thể sử dụng một chủ đề giống như bất kỳ chủ đề nào khác: bạn nhập nó và sử dụng nó như một tiện ích mở rộng:

import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import {html} from "@codemirror/lang-html"
import { oneDarkTheme } from "@codemirror/theme-one-dark";

let timer;

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      html(),
      oneDarkTheme,     
    ],    
  }),
  parent: document.body
})

Như tôi đã nói, bạn có thể sử dụng chủ đề của mình. CodeMirror 6 sử dụng CSS-in-JS.

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})

Nếu bạn không muốn cung cấp kiểu cho mọi thứ, bạn có thể mở rộng chủ đề cơ sở và chỉ ghi đè các phần của nó. Dưới đây là một ví dụ thay đổi họ và kích thước phông chữ trình chỉnh sửa:

import {EditorView} from "@codemirror/view"

const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

Gắn kết tất cả lại với nhau

Phần còn thiếu duy nhất là trình đánh giá mã, công cụ này đối với JavaScript rất dễ thực hiện (vì nó chạy trong trình duyệt). Chỉ cần thêm một số console.log()để xem kết quả thực thi mã trên bảng điều khiển DevTools của trình duyệt.

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

const jscode='';
const myTheme = EditorView.baseTheme({
  "&.cm-editor": {
    fontSize: '16px',
  },
  ".cm-scroller": {
    fontFamily:'Consolas, Menlo, Monaco, source-code-pro, Courier New, monospace'
  },
})

let timer;

const evaluateCode = (code) => {
  console.clear();
  try{
    Function(code)(window);
  }
  catch(err) {
    console.error(err);
  }
}

let editor = new EditorView({
  state: EditorState.create({
    extensions: [
      basicSetup, 
      javascript(),
      myTheme,
      EditorView.updateListener.of((v)=> {
        if(v.docChanged) {
          if(timer) clearTimeout(timer);
          timer = setTimeout(() => {
            evaluateCode(editor.state.doc.toString())
          }, 500 );
        }
      })
    ],    
    doc: jscode
  }),
  parent: document.body
})

Và đó là tất cả. Bạn có một sân chơi JavaScript tuyệt vời có thể được tích hợp vào một nền tảng lớn hơn.

 Nguồn: https://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

   #codemirror  #code #javascript 

Cách Tạo Một Trình Soạn Thảo Mã Với Codemirror 6

How to Make a Code Editor with Codemirror 6

These days hands-on, in the browser, programming language tutorials are the standard. They all provide some kind of code editor. People learn by typing some code in those editors and instant feedback tells them the outcome.

What if you want to create such a platform? Especially one that works on a touchscreen and that is accessible? You want to create something like Codecademy, Educative, or Codewars? I do so that’s why I created Live JavaScript.

But now it’s time for an upgrade, so I started to see what code editor I should use there. I tried several code editors, but one stood out: CodeMirror. Marijn Haverbeke is doing a complete rewrite of the existing code base, and the new CodeMirror is a great piece of software. I hope this small guide will encourage you to give it a try.

 See more at: https://www.raresportan.com/how-to-make-a-code-editor-with-codemirror6/

#codemirror #code #javascript 

How to Make a Code Editor with Codemirror 6
Dexter  Goodwin

Dexter Goodwin

1620391920

Styling CodeMirror v6 with Material UI

In the last post I wrapped CodeMirror v6 with a react component. However, it still looked like a plain <textarea> when it should be looking like a code editor.

CodeMirror’s  base setup has a grocery store’s worth of extensions to get started with full-fledged code editor. I have a much humbler objective, so I picked out the two extensions that I thought would be applicable to my use case:

  • @codemirror/highlight
  • @codemirror/matchbrackets

CodeMirror on its own is oblivious to code syntax and requires a set of rules to label segments of code. Fortunately, the @codemirror/lang-javascrip``t extension contains the rules for tagging JavaScript/JSX and TypeScript/TSX.

npm i \
@codemirror/highlight \
@codemirror/matchbrackets \
@codemirror/lang-javascript

I created a new file ~/components/editor/extensions.ts and import the relevant extensions. Most extensions will be the returned value of a function call. Some, like defaultHighlightStyle.fallback, will be values.

#typescript-with-react #codemirror #material-ui #typescript

Styling CodeMirror v6 with Material UI
Reid  Rohan

Reid Rohan

1619697780

Failing to add CodeMirror 6 (and then Succeeding)

In the last post I created a React component that intercepted and formatted the arguments for console.log and console.error.

In this post I’ll be upgrading from a simple <textarea> to something that is designed to show code.

What Should I Use?

After mulling over a few different options, even considering making something from scratch, I decided that  CodeMirror would be the best fit for short code snippets.

Fortunately, CodeMirror v6 is out and it’s a fantastic opportunity to try it out.

Creating the Editor Component

The editor component will live in src/components/editor/index.tsx. For now, it will be an empty react component.

import React from "react";

export const Editor = () => {
  return <></>;
};

We’ll also need to install some libraries from npm

npm i @codemirror/view @codemirror/state

CodeMirror needs to be attached to a DOM node, so we’ll add it via a ref. We’ll also use a <section> for some added semantics. In the world of TypeScript types, I learned that any HTML element that doesn’t need more than what the base interface provides, like <section>, should use the HTMLElement type.

import React from "react";

export const Editor = () => {
  const editorRef = React.useRef<HTMLElement>(null);
  return <section ref="editorRef"/>;
};

#react #codemirror #typescript #typescript-with-react

Failing to add CodeMirror 6 (and then Succeeding)