0
# Plugin System
1
2
The plugin system provides extensible architecture for adding features and functionality to PrismJS. Plugins integrate with the highlighting process through hooks and extend capabilities with additional UI elements, processing logic, and specialized highlighting features.
3
4
## Capabilities
5
6
### Plugin Namespace
7
8
The main container for all loaded plugins and plugin-related functionality.
9
10
```javascript { .api }
11
/**
12
* Object containing all loaded plugins
13
* @type {Object<string, Object>}
14
*/
15
Prism.plugins;
16
```
17
18
**Usage Examples:**
19
20
```javascript
21
// Check if plugin is loaded
22
if (Prism.plugins.lineNumbers) {
23
console.log('Line Numbers plugin is available');
24
}
25
26
// Access plugin functionality
27
Prism.plugins.fileHighlight.highlight(document);
28
29
// List all loaded plugins
30
console.log(Object.keys(Prism.plugins));
31
// Output: ['toolbar', 'lineNumbers', 'copyToClipboard', ...]
32
```
33
34
### Hook System
35
36
Event-driven system allowing plugins to integrate with the highlighting process.
37
38
```javascript { .api }
39
/**
40
* Hook system for plugin integration and event handling
41
*/
42
Prism.hooks;
43
44
/**
45
* Add callback function for specific hook event
46
* @param {string} name - Hook name (before-highlight, after-highlight, etc.)
47
* @param {function} callback - Function to execute when hook fires
48
*/
49
Prism.hooks.add(name, callback);
50
51
/**
52
* Execute all callbacks registered for specific hook
53
* @param {string} name - Hook name to execute
54
* @param {object} env - Environment object passed to all callbacks
55
*/
56
Prism.hooks.run(name, env);
57
```
58
59
**Available Hooks:**
60
61
```javascript
62
// Core highlighting hooks
63
'before-highlightall' // Before highlighting all elements
64
'before-all-elements-highlight' // Before processing element list
65
'before-sanity-check' // Before element validation
66
'before-highlight' // Before highlighting single element
67
'before-insert' // Before inserting highlighted HTML
68
'after-highlight' // After highlighting complete
69
'complete' // After DOM insertion complete
70
'wrap' // When wrapping tokens in HTML
71
72
// Token processing hooks
73
'before-tokenize' // Before tokenization starts
74
'after-tokenize' // After tokenization complete
75
```
76
77
**Usage Examples:**
78
79
```javascript
80
// Add custom processing before highlighting
81
Prism.hooks.add('before-highlight', function(env) {
82
// env.element, env.language, env.grammar, env.code
83
console.log('About to highlight:', env.language);
84
85
// Modify code before highlighting
86
env.code = env.code.trim();
87
});
88
89
// Process tokens after tokenization
90
Prism.hooks.add('after-tokenize', function(env) {
91
// env.tokens, env.code, env.grammar, env.language
92
env.tokens.forEach(token => {
93
if (token instanceof Prism.Token && token.type === 'comment') {
94
token.alias = 'faded';
95
}
96
});
97
});
98
99
// Customize HTML wrapping for tokens
100
Prism.hooks.add('wrap', function(env) {
101
// env.type, env.content, env.tag, env.attributes, env.language
102
if (env.type === 'keyword') {
103
env.attributes.title = `Keyword: ${env.content}`;
104
env.attributes['data-tooltip'] = 'This is a language keyword';
105
}
106
});
107
108
// Add custom completion logic
109
Prism.hooks.add('complete', function(env) {
110
// env.element, env.highlightedCode
111
env.element.setAttribute('data-highlighted', 'true');
112
console.log('Highlighting complete for:', env.element);
113
});
114
```
115
116
## Built-in Plugins
117
118
### Line Numbers Plugin
119
120
Adds line numbers to code blocks.
121
122
```javascript { .api }
123
/**
124
* Line Numbers plugin functionality
125
*/
126
Prism.plugins.lineNumbers;
127
128
/**
129
* Get line element for specific line number
130
* @param {Element} element - Pre element with line numbers
131
* @param {number} number - Line number to retrieve
132
* @returns {Element|undefined} Line element or undefined
133
*/
134
Prism.plugins.lineNumbers.getLine(element, number);
135
136
/**
137
* Resize line numbers to match code height
138
* @param {Element} element - Pre element to resize
139
*/
140
Prism.plugins.lineNumbers.resize(element);
141
```
142
143
**Usage Examples:**
144
145
```javascript
146
// Enable line numbers via CSS class
147
// <pre class="line-numbers"><code class="language-js">...</code></pre>
148
149
// Programmatically enable line numbers
150
const codeBlock = document.querySelector('pre');
151
codeBlock.classList.add('line-numbers');
152
Prism.highlightElement(codeBlock.querySelector('code'));
153
154
// Access specific line
155
const lineElement = Prism.plugins.lineNumbers.getLine(codeBlock, 5);
156
if (lineElement) {
157
lineElement.style.backgroundColor = 'yellow';
158
}
159
160
// Resize after dynamic content changes
161
Prism.plugins.lineNumbers.resize(codeBlock);
162
```
163
164
### Toolbar Plugin
165
166
Adds customizable toolbar to code blocks.
167
168
```javascript { .api }
169
/**
170
* Toolbar plugin functionality
171
*/
172
Prism.plugins.toolbar;
173
174
/**
175
* Register new toolbar button
176
* @param {string} key - Unique button identifier
177
* @param {Object|function} button - Button configuration or factory function
178
*/
179
Prism.plugins.toolbar.registerButton(key, button);
180
181
/**
182
* Get toolbar for specific code element
183
* @param {Element} element - Code element
184
* @returns {Element} Toolbar element
185
*/
186
Prism.plugins.toolbar.getToolbarElement(element);
187
```
188
189
**Usage Examples:**
190
191
```javascript
192
// Register custom toolbar button
193
Prism.plugins.toolbar.registerButton('select-code', {
194
text: 'Select All',
195
onClick: function(env) {
196
// env.element contains the code element
197
const range = document.createRange();
198
range.selectNodeContents(env.element);
199
window.getSelection().removeAllRanges();
200
window.getSelection().addRange(range);
201
}
202
});
203
204
// Register button with dynamic content
205
Prism.plugins.toolbar.registerButton('show-language', function(env) {
206
return {
207
text: env.language.toUpperCase(),
208
className: 'language-label',
209
};
210
});
211
212
// Access toolbar after highlighting
213
Prism.hooks.add('complete', function(env) {
214
const toolbar = Prism.plugins.toolbar.getToolbarElement(env.element);
215
if (toolbar) {
216
toolbar.style.backgroundColor = '#f0f0f0';
217
}
218
});
219
```
220
221
### Copy to Clipboard Plugin
222
223
Adds copy functionality to code blocks.
224
225
```javascript { .api }
226
/**
227
* Copy to Clipboard plugin (extends toolbar)
228
*/
229
Prism.plugins.copyToClipboard;
230
```
231
232
**Usage Examples:**
233
234
```javascript
235
// Automatically adds copy button to toolbar
236
// No additional API - works automatically with toolbar plugin
237
238
// Customize copy button text
239
Prism.hooks.add('before-highlightall', function() {
240
// This runs before the plugin initializes
241
if (typeof Prism.plugins.toolbar !== 'undefined') {
242
// Plugin will automatically register copy button
243
}
244
});
245
246
// Listen for copy events
247
document.addEventListener('prism-copy-success', function(e) {
248
console.log('Code copied:', e.detail.code);
249
});
250
251
document.addEventListener('prism-copy-error', function(e) {
252
console.error('Copy failed:', e.detail.error);
253
});
254
```
255
256
### File Highlight Plugin
257
258
Load and highlight external code files.
259
260
```javascript { .api }
261
/**
262
* File Highlight plugin functionality
263
*/
264
Prism.plugins.fileHighlight;
265
266
/**
267
* Highlight external files in pre elements with data-src
268
* @param {Element} [container=document] - Container to search within
269
*/
270
Prism.plugins.fileHighlight.highlight(container);
271
272
/**
273
* @deprecated Use Prism.plugins.fileHighlight.highlight instead
274
* Legacy function for backwards compatibility
275
* @param {Element} [container=document] - Container to search within
276
*/
277
Prism.fileHighlight(container);
278
```
279
280
**Usage Examples:**
281
282
```html
283
<!-- HTML setup for file highlighting -->
284
<pre data-src="example.js"></pre>
285
<pre data-src="styles.css" data-range="1,10"></pre>
286
<pre data-src="config.json" class="language-json"></pre>
287
```
288
289
```javascript
290
// Manual file highlighting
291
Prism.plugins.fileHighlight.highlight(document);
292
293
// Highlight files in specific container
294
const container = document.getElementById('code-examples');
295
Prism.plugins.fileHighlight.highlight(container);
296
297
// Files are loaded asynchronously
298
Prism.hooks.add('complete', function(env) {
299
if (env.element.hasAttribute('data-src')) {
300
console.log('External file highlighted:', env.element.getAttribute('data-src'));
301
}
302
});
303
```
304
305
### Autoloader Plugin
306
307
Automatically load language definitions when needed.
308
309
```javascript { .api }
310
/**
311
* Autoloader plugin functionality
312
*/
313
Prism.plugins.autoloader;
314
315
/**
316
* Set path template for loading language files
317
* @param {string} template - Path template with {id} placeholder
318
*/
319
Prism.plugins.autoloader.languages_path;
320
321
/**
322
* Load specific languages
323
* @param {string|string[]} languages - Language IDs to load
324
* @param {function} [success] - Success callback
325
* @param {function} [error] - Error callback
326
*/
327
Prism.plugins.autoloader.loadLanguages(languages, success, error);
328
```
329
330
**Usage Examples:**
331
332
```javascript
333
// Configure autoloader path
334
Prism.plugins.autoloader.languages_path = 'components/prism-{id}.min.js';
335
336
// Languages are loaded automatically when encountered
337
// <pre><code class="language-python">print("Hello")</code></pre>
338
// Python grammar will be loaded automatically
339
340
// Manual language loading
341
Prism.plugins.autoloader.loadLanguages(['python', 'rust'], function() {
342
console.log('Languages loaded successfully');
343
// Now highlight code
344
Prism.highlightAll();
345
}, function() {
346
console.error('Failed to load languages');
347
});
348
349
// Load single language
350
Prism.plugins.autoloader.loadLanguages('go');
351
```
352
353
### Additional Plugins
354
355
```javascript { .api }
356
/**
357
* Line Highlight - Highlight specific lines
358
*/
359
Prism.plugins.lineHighlight;
360
361
/**
362
* Match Braces - Highlight matching brackets
363
*/
364
Prism.plugins.matchBraces;
365
366
/**
367
* Show Invisibles - Display whitespace characters
368
*/
369
Prism.plugins.showInvisibles;
370
371
/**
372
* Normalize Whitespace - Clean up code formatting
373
*/
374
Prism.plugins.normalizeWhitespace;
375
376
/**
377
* Command Line - Style command-line sessions
378
*/
379
Prism.plugins.commandLine;
380
381
/**
382
* Diff Highlight - Highlight code differences
383
*/
384
Prism.plugins.diffHighlight;
385
```
386
387
## Plugin Development
388
389
### Creating Custom Plugins
390
391
```javascript
392
// Basic plugin structure
393
(function() {
394
if (typeof Prism === 'undefined' || typeof document === 'undefined') {
395
return;
396
}
397
398
// Plugin configuration
399
var config = Prism.plugins.myPlugin = {
400
setting1: 'default value',
401
setting2: true
402
};
403
404
// Plugin functionality
405
function doSomething(element) {
406
// Plugin logic
407
element.classList.add('my-plugin-processed');
408
}
409
410
// Hook into highlighting process
411
Prism.hooks.add('complete', function(env) {
412
if (env.element.classList.contains('my-plugin-enabled')) {
413
doSomething(env.element);
414
}
415
});
416
})();
417
```
418
419
### Plugin Best Practices
420
421
```javascript
422
// Environment detection
423
if (typeof Prism === 'undefined' || typeof document === 'undefined') {
424
return; // Exit if not in browser with Prism
425
}
426
427
// Feature detection
428
if (!document.querySelector) {
429
return; // Exit if required DOM methods unavailable
430
}
431
432
// Plugin namespace
433
var MyPlugin = Prism.plugins.myPlugin = {
434
// Public API
435
process: function(element) {
436
// Implementation
437
},
438
439
// Configuration
440
config: {
441
option1: true,
442
option2: 'default'
443
}
444
};
445
446
// Safe hook registration
447
Prism.hooks.add('complete', function(env) {
448
try {
449
MyPlugin.process(env.element);
450
} catch (error) {
451
console.error('MyPlugin error:', error);
452
}
453
});
454
```
455
456
### Plugin Integration Patterns
457
458
```javascript
459
// Toolbar integration
460
if (Prism.plugins.toolbar) {
461
Prism.plugins.toolbar.registerButton('my-button', {
462
text: 'My Action',
463
onClick: function(env) {
464
// Button click handler
465
console.log('Button clicked for:', env.language);
466
}
467
});
468
}
469
470
// CSS class detection
471
Prism.hooks.add('before-sanity-check', function(env) {
472
if (env.element.classList.contains('my-plugin')) {
473
// Plugin-specific processing
474
env.element.setAttribute('data-processed', 'my-plugin');
475
}
476
});
477
478
// Language-specific plugins
479
Prism.hooks.add('after-tokenize', function(env) {
480
if (env.language === 'javascript') {
481
// JavaScript-specific token processing
482
processJavaScriptTokens(env.tokens);
483
}
484
});
485
```
486
487
## Plugin Loading and Dependencies
488
489
```javascript
490
// Check plugin dependencies
491
function hasRequiredPlugins() {
492
return Prism.plugins.toolbar && Prism.plugins.lineNumbers;
493
}
494
495
if (hasRequiredPlugins()) {
496
// Initialize plugin that depends on others
497
initializeComplexPlugin();
498
} else {
499
console.warn('Required plugins not loaded');
500
}
501
502
// Dynamic plugin loading
503
function loadPlugin(pluginPath, callback) {
504
const script = document.createElement('script');
505
script.src = pluginPath;
506
script.onload = callback;
507
script.onerror = function() {
508
console.error('Failed to load plugin:', pluginPath);
509
};
510
document.head.appendChild(script);
511
}
512
513
// Load plugin chain
514
loadPlugin('plugins/toolbar/prism-toolbar.js', function() {
515
loadPlugin('plugins/copy-to-clipboard/prism-copy-to-clipboard.js', function() {
516
console.log('All plugins loaded');
517
Prism.highlightAll();
518
});
519
});
520
```