Monaco Editor

repository·main·Indexed 11 days ago

https://github.com/microsoft/monaco-editor

A high-performance, feature-rich browser-based code editor that powers VS Code. Version 0.56.0. Includes support for ESM and AMD loading, a dedicated Webpack plugin for bundle optimization, and an alpha-stage LSP client (monaco-lsp-client) for connecting to external language servers.

Tokens
14.4K
Snippets
41
Records
55
Agent score
99%

What's inside Monaco Editor

  1. Overview of Monaco LSP Client

    main

    The monaco-lsp-client package provides a Language Server Protocol (LSP) client implementation specifically designed for use with the Monaco Editor. This allows you to connect Monaco to external language servers to provide advanced features like autocompletion, diagnostics, and hover information.

    Note: This package is currently in alpha stage and may contain bugs.

  2. Clean up resources using Disposables

    main

    Many objects in Monaco implement a .dispose() method. To prevent memory leaks and resource exhaustion, you must call .dispose() when an object is no longer needed:

    • Call model.dispose() to unregister a model and free up its URI for reuse.
    • Dispose of Editors to free up resources and remove their model listeners.
  3. How Models, URIs, Editors, and Providers work together

    main

    Understanding the core abstractions of Monaco Editor is essential for effective usage:

    • Models: The heart of the editor. A model represents a single file's content, its language, and its edit history. You interact with models to manage content.
    • URIs: Every model is uniquely identified by a URI. You should treat the editor as a virtual file system. If you don't provide a URI, Monaco assigns one using the pattern inmemory://model/[number].
    • Editors: The user-facing view of a model. An editor is attached to a DOM element and is responsible for displaying the model, managing view state, and executing commands.
    • Providers: These enable smart features like autocompletion and hover information. Providers operate on models and often rely on the model's URI (e.g., for TypeScript import resolution or JSON schema application).

    Relationship Summary: An Editor displays a Model. The Model is identified by a URI. Providers use the Model and its URI to supply intelligence.

  4. Explore Monaco Editor loading variations

    main

    The samples repository includes different ways to load and run the Monaco Editor depending on your environment and module system:

    • AMD Lazy Loading: browser-amd-editor (browser-based).
    • AMD Synchronous Loading: browser-script-editor (using <script> tags in a browser).
    • Webpack: browser-esm-webpack (standard) and browser-esm-webpack-small (a subset of the editor).
    • Electron: electron-amd.
    • NW.js: nwjs-amd and nwjs-amd-v2 (Note: v2 is reported to work, while the initial version may not).
  5. Integrate Monaco Editor using Monaco Editor Webpack Plugin

    main

    The easiest way to integrate the ESM version of Monaco Editor with Webpack is by using the community-authored monaco-editor-webpack-plugin. This plugin allows you to pass options to select only specific editor features or languages, helping to reduce bundle size.

    To use it, install the plugin and add it to your webpack.config.js plugins array. Ensure you have loaders for .css and .ttf files configured.

    // index.js
    import * as monaco from 'monaco-editor';
    
    monaco.editor.create(document.getElementById('container'), {
    	value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
    	language: 'javascript'
    });
    
    // webpack.config.js
    const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
    const path = require('path');
    
    module.exports = {
    	entry: './index.js',
    	output: {
    		path: path.resolve(__dirname, 'dist'),
    		filename: 'app.js'
    	},
    	module: {
    		rules: [
    			{
    				test: /\.css$/, 
    				use: ['style-loader', 'css-loader']
    			},
    			{
    				test: /\.ttf$/, 
    				use: ['file-loader']
    			}
    		]
    	},
    	plugins: [new MonacoWebpackPlugin()]
    };
  6. Explore Monaco Editor feature examples and techniques

    main

    Beyond basic loading, the samples demonstrate specific integration techniques:

    • Diff Editor: browser-amd-diff-editor shows how to run the diff editor in a browser.
    • Iframe Integration: browser-amd-iframe shows how to run the editor within an <iframe>.
    • Localization: browser-amd-localized demonstrates running the editor with a specific locale (e.g., German).
    • Custom Grammars: browser-amd-monarch shows how to use a custom language grammar written with the Monarch system.
    • Shared Models: browser-amd-shared-model demonstrates how to use the same text model across two different editor instances.
  7. Run Monaco Editor Samples locally

    main

    To run the standalone HTML samples provided in the repository, clone the repository, navigate to the samples directory, install dependencies, and start the local server.

    1. Clone the repository.
    2. Navigate to the samples folder.
    3. Install dependencies using npm install .".
    4. Start the server using npm run simpleserver.

    Once running, access the samples at http://localhost:8888.

    git clone https://github.com/microsoft/monaco-editor.git
    cd monaco-editor
    cd samples
    npm install .
    npm run simpleserver
  8. Configure MonacoWebpackPlugin in webpack.config.js

    main

    Add MonacoWebpackPlugin to your Webpack configuration. Ensure you have rules to handle .css and .ttf files. For Webpack 5+, use Asset Modules for fonts. For Webpack 4 or lower, use file-loader for .ttf files.

    const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
    const path = require('path');
    
    module.exports = {
    	entry: './index.js',
    	output: {
    		path: path.resolve(__dirname, 'dist'),
    		filename: 'app.js'
    	},
    	module: {
    		rules: [
    			{
    				test: /\.css$/,
    				use: ['style-loader', 'css-loader']
    			},
    			{
    				test: /\.ttf$/,
    			type: 'asset/resource'
    			}
    		]
    	},
    	plugins: [new MonacoWebpackPlugin()]
    };
  9. Localize the editor with nls scripts

    main

    To load the editor in a specific language, you must load the corresponding nls.messages.[language].js script before the main monaco editor script.

    For example, to load the German translation, include:

    <script src="path/to/monaco-editor/esm/nls.messages.de.js"></script>
  10. Integrate Monaco Editor using plain Webpack

    main

    If you prefer not to use the specialized Webpack plugin, you can configure Webpack manually. This requires two main steps:

    1. Configure Webpack Entry Points: You must explicitly define entry points for the main application and each language worker (e.g., json.worker, css.worker, ts.worker) by pointing to the corresponding files in monaco-editor/esm/vs/....
    2. Configure self.MonacoEnvironment: In your application code, you must implement self.MonacoEnvironment.getWorkerUrl to map language labels to the specific bundle filenames you created in your Webpack configuration.

    Note: Set output.globalObject to 'self' in your Webpack config to ensure compatibility with web workers.

    // index.js
    import * as monaco from 'monaco-editor';
    
    self.MonacoEnvironment = {
    	getWorkerUrl: function (moduleId, label) {
    		if (label === 'json') {
    			return './json.worker.bundle.js';
    		}
    		if (label === 'css' || label === 'scss' || label === 'less') {
    			return './css.worker.bundle.js';
    		}
    		if (label === 'html' || label === 'handlebars' || label === 'razor') {
    			return './html.worker.bundle.js';
    		}
    		if (label === 'typescript' || label === 'javascript') {
    			return './ts.worker.bundle.js';
    		}
    		return './editor.worker.bundle.js';
    	}
    };
    
    monaco.editor.create(document.getElementById('container'), {
    	value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
    	language: 'javascript'
    });
    
    // webpack.config.js
    const path = require('path');
    
    module.exports = {
    	entry: {
    		app: './index.js',
    		'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js',
    		'json.worker': 'monaco-editor/esm/vs/language/json/json.worker',
    		'css.worker': 'monaco-editor/esm/vs/language/css/css.worker',
    		'html.worker': 'monaco-editor/esm/vs/language/html/html.worker',
    		'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker'
    	},
    	output: {
    		globalObject: 'self',
    		filename: '[name].bundle.js',
    		path: path.resolve(__dirname, 'dist')
    	},
    	module: {
    		rules: [
    			{
    				test: /\.css$/, 
    				use: ['style-loader', 'css-loader']
    			},
    			{
    				test: /\.ttf$/, 
    				use: ['file-loader']
    			}
    		]
    	}
    };
  11. Install monaco-editor via npm

    main

    Install the Monaco Editor using npm. The package includes an ESM version in the /esm directory (recommended for modern bundlers like Webpack) and the monaco.d.ts file which defines the public API.

    Note: The AMD build is provided for backwards compatibility but is deprecated and will be removed in future versions.

    npm install monaco-editor
  12. Integrate Monaco Editor using Parcel

    main

    When using Parcel, you must build the language workers separately from your main source code.

    1. Implement self.MonacoEnvironment.getWorkerUrl: Map language labels to the expected worker filenames (e.g., ./json.worker.js).
    2. Build Workers: Use a script (like a bash script) to run parcel build on each individual worker file located in node_modules/monaco-editor/esm/vs/....
    3. Execution: Run the build script followed by parcel index.html. This ensures workers are built into your distribution directory.

    Note: The paths in getWorkerUrl are relative to the build directory of your main bundle.

    // index.js
    import * as monaco from 'monaco-editor';
    
    self.MonacoEnvironment = {
    	getWorkerUrl: function (moduleId, label) {
    		if (label === 'json') {
    			return './json.worker.js';
    		}
    		if (label === 'css' || label === 'scss' || label === 'less') {
    			return './css.worker.js';
    		}
    		if (label === 'html' || label === 'handlebars' || label === 'razor') {
    			return './html.worker.js';
    		}
    		if (label === 'typescript' || label === 'javascript') {
    			return './ts.worker.js';
    		}
    		return './editor.worker.js';
    	}
    };
    
    monaco.editor.create(document.getElementById('container'), {
    	value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
    	language: 'javascript'
    });
    
    // build_workers.sh
    ROOT=$PWD/node_modules/monaco-editor/esm/vs
    OPTS="--no-source-maps --log-level 1"
    
    parcel build $ROOT/language/json/json.worker.js $OPTS
    parcel build $ROOT/language/css/css.worker.js $OPTS
    parcel build $ROOT/language/html/html.worker.js $OPTS
    parcel build $ROOT/language/typescript/ts.worker.js $OPTS
    parcel build $ROOT/editor/editor.worker.js $OPTS