CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-emmet

Essential toolkit for web developers providing abbreviation expansion for HTML and CSS

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

abbreviation-extraction.mddocs/

Abbreviation Extraction

Intelligent extraction of Emmet abbreviations from source code at specified positions, with support for prefix matching and auto-completion scenarios.

Capabilities

Extract Function

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); // undefined

Advanced Extraction Options

Look Ahead Option

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

Syntax Type Option

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 }

Prefix Option

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 }

Integration with Expansion

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

Error Handling

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

Install with Tessl CLI

npx tessl i tessl/npm-emmet

docs

abbreviation-extraction.md

configuration.md

core-expansion.md

index.md

markup-processing.md

stylesheet-processing.md

tile.json