@lobehub/editor
Version:
A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.
267 lines (254 loc) • 8.73 kB
JavaScript
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
/**
* Language detection utility for code snippets
* Uses pattern matching and heuristics to identify programming languages
*/
var LANGUAGE_PATTERNS = {
// Shell/Config
bash: {
optional: [/\|\||&&/, /echo\s+/, /if\s+\[/],
required: [/^#!\/bin\/(ba)?sh/, /\${?\w+}?/],
weight: 7
},
csharp: {
optional: [/\[.*]/, /async\s+Task/, /var\s+\w+\s*=/],
required: [/(public|private|protected)\s+(class|interface|namespace)/, /using\s+\w+/],
weight: 8
},
css: {
optional: [/@media|@import|@keyframes/, /!important/],
required: [/[#.]?[\w-]+\s*{/, /:\s*[\w#%-]+;/],
weight: 8
},
dockerfile: {
required: [/^FROM\s+/, /^(RUN|CMD|COPY|ADD|WORKDIR|ENV|EXPOSE)\s+/m],
weight: 10
},
go: {
optional: [/import\s+\(/, /:=/, /defer|goroutine/],
required: [/package\s+\w+/, /func\s+\w+/],
weight: 8
},
graphql: {
optional: [/fragment\s+/, /on\s+\w+/],
required: [/(query|mutation|subscription)\s+/, /{[\S\s]*}/],
weight: 8
},
// Web languages
html: {
optional: [/<\/\w+>/, /class=|id=/],
required: [/<(html|head|body|div|span|p|a|img|script|style|link|meta)/i],
weight: 9
},
ini: {
exclude: [/^\s*</, /{.*}/],
required: [/^\[.*]$/m, /\w+\s*=\s*/],
weight: 6
},
java: {
optional: [/@Override|@Autowired/, /extends|implements/, /System\.out/],
required: [/(public|private|protected)\s+(class|interface|static)/, /\w+\s+\w+\s*\([^)]*\)\s*{/],
weight: 8
},
// JavaScript family
javascript: {
exclude: [/^\s*</, /interface\s+\w+/, /:\s*\w+\s*[;=]/],
optional: [/=>\s*{/, /console\.(log|error|warn)/, /\.then\(/, /require\(/],
required: [/(function|const|let|var|class|import|export|async|await)\s/],
weight: 7
},
// Data formats
json: {
optional: [/"[^"]*"\s*:/, /:\s*["'[{]/],
required: [/^\s*[[{]/, /[\]}]\s*$/],
weight: 10
},
jsx: {
exclude: [/^\s*<!DOCTYPE/, /^\s*<html/],
optional: [/className=/, /{.*}/, /import.*from/],
required: [/<\w+[^>]*>/, /(const|function|class)\s+\w+/],
weight: 8
},
makefile: {
optional: [/\$\(.*\)/, /\.PHONY/],
required: [/^[\w-]+:\s*/, /^\t/m],
weight: 7
},
// Other common languages
markdown: {
optional: [/```/, /\*\*.*\*\*/, /^\s*[*+-]\s+/m],
required: [/^#{1,6}\s+/, /\[.*]\(.*\)/],
weight: 6
},
php: {
optional: [/function\s+\w+/, /class\s+\w+/, /echo|print/],
required: [/<\?php/, /\$\w+/],
weight: 10
},
powershell: {
optional: [/\|\s*Where/, /\|\s*Select/, /param\(/],
required: [/\$\w+/, /(Get|Set|New|Remove)-\w+/],
weight: 8
},
// Backend languages
python: {
optional: [/if __name__|print\(/, /self\.|lambda/, /@\w+\s*$/m],
required: [/(def|class|import|from)\s+\w+/, /:\s*$/m],
weight: 8
},
ruby: {
optional: [/do\s*\|.*\|/, /puts|require/, /:\w+\s*=>/],
required: [/(def|class|module|end)\s/, /@\w+/],
weight: 7
},
rust: {
optional: [/::\w+/, /&mut|&str/, /#\[derive\(/],
required: [/(fn|struct|impl|trait|use)\s+/, /let\s+(mut\s+)?\w+/],
weight: 8
},
scss: {
optional: [/@mixin|@include|@extend/, /#{.*}/],
required: [/\$[\w-]+\s*:/, /[&@]\w+/],
weight: 8
},
// Database
sql: {
optional: [/from|where|join|group by|order by/i, /\*/],
required: [/(select|insert|update|delete|create|alter|drop)\s+/i],
weight: 9
},
// Config formats
toml: {
optional: [/\[\[.*]]/, /"""[\S\s]*"""/],
required: [/^\[.*]$/m, /\w+\s*=\s*[\w"']/],
weight: 8
},
tsx: {
optional: [/:\s*React\./, /:\s*FC</, /useState|useEffect/],
required: [/<\w+[^>]*>/, /interface|type\s+\w+/],
weight: 9
},
typescript: {
optional: [/<\w+>/, /as\s+\w+/, /implements|extends/],
required: [/(interface|type|enum)\s+\w+/, /:\s*\w+(\[]|<.+>)?\s*[);=]/],
weight: 8
},
xml: {
optional: [/<\/\w+>/, /xmlns/],
required: [/<\?xml|<\w+[^>]*>/],
weight: 9
},
yaml: {
exclude: [/^\s*[[\]{]/],
required: [/^[\s-]*\w+:\s*/, /^[\s-]*-\s+/m],
weight: 8
}
};
/**
* Detect the programming language of a code snippet
* @param code - The code snippet to analyze
* @returns Language detection result with confidence score
*/
export function detectLanguage(code) {
if (!code || code.trim().length === 0) {
return null;
}
var trimmed = code.trim();
var scores = {};
// Test each language pattern
for (var _i = 0, _Object$entries = Object.entries(LANGUAGE_PATTERNS); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
language = _Object$entries$_i[0],
pattern = _Object$entries$_i[1];
var score = 0;
var baseWeight = pattern.weight || 5;
// Check exclude patterns first
if (pattern.exclude) {
var hasExclude = pattern.exclude.some(function (regex) {
return regex.test(trimmed);
});
if (hasExclude) {
continue; // Skip this language
}
}
// Check required patterns
if (pattern.required) {
var allRequired = pattern.required.every(function (regex) {
return regex.test(trimmed);
});
if (!allRequired) {
continue; // All required patterns must match
}
score += baseWeight * 2; // Strong indicator
}
// Check optional patterns
if (pattern.optional) {
var matchedOptional = pattern.optional.filter(function (regex) {
return regex.test(trimmed);
}).length;
score += matchedOptional * baseWeight;
}
if (score > 0) {
scores[language] = score;
}
}
// Find language with highest score
var entries = Object.entries(scores);
if (entries.length === 0) {
return null;
}
entries.sort(function (a, b) {
return b[1] - a[1];
});
var _entries$ = _slicedToArray(entries[0], 2),
topLanguage = _entries$[0],
topScore = _entries$[1];
// Calculate confidence (0-100)
var maxPossibleScore = (LANGUAGE_PATTERNS[topLanguage].weight || 5) * 5;
var confidence = Math.min(100, Math.round(topScore / maxPossibleScore * 100));
// Only return if confidence is high enough
if (confidence < 30) {
return null;
}
return {
confidence: confidence,
language: topLanguage
};
}
/**
* Simple detection for common formats with high confidence
* Falls back to detectLanguage for more complex detection
*/
export function detectCodeLanguage(code) {
var trimmed = code.trim();
// Fast path for JSON
if (trimmed.startsWith('{') && trimmed.endsWith('}') || trimmed.startsWith('[') && trimmed.endsWith(']')) {
try {
JSON.parse(trimmed);
return 'json';
} catch (_unused) {
// Not valid JSON
}
}
// Fast path for common patterns
if (/^\s*(select|insert|update|delete|create|alter|drop)\s+/i.test(code)) {
return 'sql';
}
if (/^<\?xml/i.test(code)) {
return 'xml';
}
if (/^FROM\s+|^RUN\s+|^CMD\s+/m.test(code)) {
return 'dockerfile';
}
if (/<\?php/.test(code)) {
return 'php';
}
// Use pattern matching for complex detection
var result = detectLanguage(code);
return result && result.confidence > 50 ? result.language : null;
}