Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,12 @@ export class Syntax {
} catch (error) {
throw new LanguageLoadError(name, path, {cause: error});
}

// If the module exports a register function, call it with this instance
if (typeof module.default === 'function') {
module.default(this);
}

// After calling register, aliases have been registered. Re-resolve the name:
let resolvedName = this.#aliases[name] || name;
return loader.get(resolvedName);
Expand Down
24 changes: 11 additions & 13 deletions Syntax/CodeElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class CodeElement extends HTMLElement {

constructor() {
super();

/**
* A promise that resolves when the current highlighting attempt completes.
* Check `highlighted` before using line measurement APIs.
Expand All @@ -105,10 +105,7 @@ export class CodeElement extends HTMLElement {
}

get language() {
return (
this.getAttribute('language') ||
this.#detectLanguageFromClass()
);
return this.getAttribute('language') || this.#detectLanguageFromClass();
}

set language(value) {
Expand Down Expand Up @@ -150,26 +147,26 @@ export class CodeElement extends HTMLElement {
*/
getLineBoundingClientRect(lineNumber) {
if (!this.#shadow) return null;

const code = this.#shadow.querySelector('code');
if (!code) return null;

const lines = code.children;
if (lineNumber < 1 || lineNumber > lines.length) return null;

return lines[lineNumber - 1].getBoundingClientRect();
}

/**
* Get the total number of rendered lines.
* @returns {number} The line count, or 0 if not yet rendered.
*/
get lineCount() {
if (!this.#shadow) return 0;

const code = this.#shadow.querySelector('code');
if (!code) return 0;

return code.children.length;
}

Expand All @@ -180,7 +177,7 @@ export class CodeElement extends HTMLElement {
get highlighted() {
return this.#highlighted;
}

connectedCallback() {
// Detect if we're inside a <pre> element and set wrap attribute
if (this.parentElement?.tagName === 'PRE') {
Expand Down Expand Up @@ -382,7 +379,8 @@ export function upgradeAll(selector, syntax = null) {
}

// Try to detect language from various sources
let language = element.getAttribute('lang') || element.getAttribute('language');
let language =
element.getAttribute('lang') || element.getAttribute('language');

if (!language) {
// Check class names
Expand Down
5 changes: 4 additions & 1 deletion Syntax/Language/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ language.push({
pattern: /<script(\s+[^>]*?)?>((.|\n)*?)<\/script>/im,
matches: Rule.extractConditionalMatch(1, 2, [
{pattern: /type\s*=\s*["']importmap["']/i, language: 'json'},
{pattern: /type\s*=\s*["'](?:text|application)\/javascript["']/i, language: 'javascript'},
{
pattern: /type\s*=\s*["'](?:text|application)\/javascript["']/i,
language: 'javascript'
},
{language: 'javascript'} // Fallback: no type or unknown type defaults to JavaScript (HTML5)
])
});
Expand Down
9 changes: 6 additions & 3 deletions Syntax/Language/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ language.push(
);

// Operators
language.push(['+', '*', '/', '-', '&', '|', '~', '!', '%', '<', '=', '>', '...'], {
type: 'operator'
});
language.push(
['+', '*', '/', '-', '&', '|', '~', '!', '%', '<', '=', '>', '...'],
{
type: 'operator'
}
);

// Access modifiers
language.push(
Expand Down
6 changes: 1 addition & 5 deletions Syntax/Language/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ language.push({
// Object keys (strings followed by colon)
language.push({
pattern: /("(?:[^"\\]|\\.)*")(\s*)(:)/,
matches: Rule.extractMatches(
{type: 'key'},
null,
{type: 'operator'}
)
matches: Rule.extractMatches({type: 'key'}, null, {type: 'operator'})
});

// Structural characters
Expand Down
17 changes: 16 additions & 1 deletion Syntax/Language/ruby.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,22 @@ const keywords = [
'block_given?'
];

const operators = ['+', '*', '/', '-', '&', '|', '~', '!', '%', '<', '=', '>', '...', '..'];
const operators = [
'+',
'*',
'/',
'-',
'&',
'|',
'~',
'!',
'%',
'<',
'=',
'>',
'...',
'..'
];
const values = ['self', 'super', 'true', 'false', 'nil'];
const access = ['private', 'protected', 'public'];

Expand Down
6 changes: 1 addition & 5 deletions Syntax/Match.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,7 @@ export class Match {

// Element-backed matches describe authored markup and must retain their
// original expression so reduction can recreate that element.
if (
this.expression &&
this.expression.owner &&
!match.expression.element
) {
if (this.expression && this.expression.owner && !match.expression.element) {
match.expression =
this.expression.owner.getRuleForType(match.expression.type) ||
match.expression;
Expand Down
10 changes: 5 additions & 5 deletions Syntax/Rule.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,14 @@ export class Rule {
* Create a conditional matcher that selects a rule based on another capture group.
* Tests a condition capture group against patterns to determine which rule to apply
* to a content capture group.
*
*
* @param {number} conditionIndex - Capture group index to test against patterns
* @param {number} contentIndex - Capture group index containing content to match
* @param {Array<{pattern?: RegExp, ...rule}>} conditions - Array of condition objects. Each can have:
* - pattern: RegExp to test against the condition group (optional - if omitted, acts as fallback)
* - Any rule properties (language, type, etc.) to apply when pattern matches
* @returns {Function} A matches function for use in language rules
*
*
* @example
* // Script tags with type-based language selection
* language.push({
Expand All @@ -152,7 +152,7 @@ export class Rule {
* {language: 'javascript'} // Fallback for no type or unknown types
* ])
* });
*
*
* @example
* // Code fence with language specifier
* language.push({
Expand All @@ -163,7 +163,7 @@ export class Rule {
* {language: 'plaintext'} // Fallback
* ])
* });
*
*
* @example
* // Conditional type based on prefix
* language.push({
Expand Down Expand Up @@ -201,7 +201,7 @@ export class Rule {

// Build syntax tree or create match based on rule properties
const offset = match.index + match[0].indexOf(content);

if (ruleProps.language) {
return [
await Language.buildTree(
Expand Down
17 changes: 9 additions & 8 deletions bin/syntax-ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,36 @@ import Syntax from '../Syntax.js';

async function main() {
const args = process.argv.slice(2);

if (args.length < 2) {
console.error('Usage: syntax-ast <language> <code>');
console.error('Example: syntax-ast javascript "const x = 1;"');
process.exit(1);
}

const [languageName, code] = args;

const syntax = new Syntax();

try {
const language = await syntax.getLanguage(languageName);
const matches = await language.getMatches(syntax, code);

console.log('Language:', languageName);
console.log('Code:', JSON.stringify(code));
console.log('\nMatches:', matches.length);
console.log('─'.repeat(80));

for (const match of matches) {
const text = code.substring(match.offset, match.offset + match.length);
console.log(`[${match.type}] @${match.offset}..${match.offset + match.length} (${match.length} chars)`);
console.log(
`[${match.type}] @${match.offset}..${match.offset + match.length} (${match.length} chars)`
);
console.log(` Text: ${JSON.stringify(text)}`);
if (match.children && match.children.length > 0) {
console.log(` Children: ${match.children.length}`);
}
}

} catch (error) {
console.error('Error:', error.message);
process.exit(1);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
],
"scripts": {
"test": "node --test 'test/**/*.js'",
"format": "prettier --write '**/*.{js,json,md}'",
"format:check": "prettier --check '**/*.{js,json,md}'"
"format": "prettier --write '**/*.{js,json}'",
"format:check": "prettier --check '**/*.{js,json}'"
},
"keywords": [
"syntax",
Expand Down
Loading