Intelligent extraction of Emmet abbreviations from source code at specified positions, with support for prefix matching and auto-completion scenarios.
Extracts Emmet abbreviation from a line of text at a specified position, searching backward from the position to find abbreviation boundaries.
/**
* Extracts Emmet abbreviation from given string
* @param line - Text line where abbreviation should be extracted
* @param pos - Caret position in line (defaults to end of line)
* @param options - Extraction configuration options
* @returns Extracted abbreviation data or undefined if no abbreviation found
*/
function extract(
line: string,
pos?: number,
options?: Partial<ExtractOptions>
): ExtractedAbbreviation | undefined;
interface ExtractOptions {
/** Look ahead for auto-closed characters like quotes and brackets */
lookAhead: boolean;
/** Type of syntax context ('markup' or 'stylesheet') */
type: SyntaxType;
/** Required prefix that must precede the abbreviation */
prefix: string;
}
interface ExtractedAbbreviation {
/** The extracted abbreviation string */
abbreviation: string;
/** Character position where abbreviation starts */
location: number;
/** Start position including any prefix */
start: number;
/** End position of the abbreviation */
end: number;
}Basic Usage Examples:
import { extract } from "emmet";
// Extract from end of line
const result1 = extract("Hello world ul.tabs>li");
console.log(result1);
// { abbreviation: "ul.tabs>li", location: 12, start: 12, end: 23 }
// Extract from specific position
const result2 = extract("div.container ul>li", 15);
console.log(result2);
// { abbreviation: "ul>li", location: 14, start: 14, end: 19 }
// No abbreviation found
const result3 = extract("Just plain text here");
console.log(result3); // undefinedHandles auto-inserted closing characters that editors commonly add when typing.
interface ExtractOptions {
/**
* Allow parser to look ahead of position for missing abbreviation parts.
* Enabled by default to handle auto-inserted closing braces, quotes, etc.
*/
lookAhead: boolean;
}Look Ahead Examples:
import { extract } from "emmet";
const source = 'ul>li[title="Foo"]'; // Cursor after "Foo", before closing quote
const pos = 15; // Position after "Foo"
// With lookAhead enabled (default)
const result1 = extract(source, pos);
console.log(result1);
// { abbreviation: 'ul>li[title="Foo"]', location: 0, start: 0, end: 17 }
// With lookAhead disabled
const result2 = extract(source, pos, { lookAhead: false });
console.log(result2);
// { abbreviation: 'Foo', location: 12, start: 12, end: 15 }Determines how to interpret bracket characters based on markup vs stylesheet context.
interface ExtractOptions {
/**
* Type of context syntax of expanded abbreviation.
* In 'stylesheet' syntax, brackets [] and {} are not supported
*/
type: SyntaxType;
}
type SyntaxType = 'markup' | 'stylesheet';Syntax Type Examples:
import { extract } from "emmet";
const source = 'a{b}';
const pos = 3; // After "b"
// Markup context (default) - includes braces as text content
const markupResult = extract(source, pos, { type: 'markup' });
console.log(markupResult);
// { abbreviation: 'a{b}', location: 0, start: 0, end: 4 }
// Stylesheet context - braces not part of abbreviation syntax
const cssResult = extract(source, pos, { type: 'stylesheet' });
console.log(cssResult);
// { abbreviation: 'b', location: 2, start: 2, end: 3 }Requires abbreviation to be preceded by a specific prefix string, useful for preventing false positives in JSX and similar contexts.
interface ExtractOptions {
/**
* String that should precede abbreviation for successful extraction.
* Useful for avoiding conflicts with variable names in JSX/JS contexts.
*/
prefix: string;
}Prefix Examples:
import { extract } from "emmet";
const jsxSource1 = '() => div'; // Variable name, not abbreviation
const jsxSource2 = '() => <div'; // JSX tag, valid abbreviation
// Without prefix - finds 'div' in both cases
extract(jsxSource1, jsxSource1.length);
// { abbreviation: 'div', location: 6, start: 6, end: 9 }
extract(jsxSource2, jsxSource2.length);
// { abbreviation: 'div', location: 7, start: 7, end: 10 }
// With prefix - only finds 'div' when preceded by '<'
extract(jsxSource1, jsxSource1.length, { prefix: '<' });
// undefined (no match)
extract(jsxSource2, jsxSource2.length, { prefix: '<' });
// { abbreviation: 'div', location: 7, start: 6, end: 10 }The extract function is designed to work seamlessly with the main expand function for editor integration scenarios.
Complete Workflow Example:
import expand, { extract } from "emmet";
function expandAbbreviationAtCursor(sourceCode: string, cursorPos: number) {
// Extract abbreviation at cursor position
const extracted = extract(sourceCode, cursorPos, {
lookAhead: true,
type: 'markup',
prefix: '' // No prefix required
});
if (!extracted) {
return null; // No abbreviation found
}
// Expand the extracted abbreviation
const expanded = expand(extracted.abbreviation);
// Return replacement info
return {
expanded,
start: extracted.start,
end: extracted.end,
original: extracted.abbreviation
};
}
// Usage in editor context
const code = "const template = ul.nav>li*3";
const cursor = code.length; // At end of line
const result = expandAbbreviationAtCursor(code, cursor);
if (result) {
console.log(`Replace "${result.original}" with:`);
console.log(result.expanded);
// Replace "ul.nav>li*3" with:
// <ul class="nav"><li></li><li></li><li></li></ul>
}The extract function returns undefined when no valid abbreviation is found rather than throwing errors.
import { extract } from "emmet";
// Various cases that return undefined
console.log(extract("")); // Empty string
console.log(extract("just text")); // No abbreviation pattern
console.log(extract("div", 0, { prefix: "<" })); // Prefix not found
console.log(extract("a{", 2)); // Incomplete abbreviation
// Always check for undefined before using result
const source = "Some code here";
const result = extract(source, source.length);
if (result) {
console.log(`Found: ${result.abbreviation}`);
} else {
console.log("No abbreviation found");
}