@lobehub/editor 1.17.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4,3 +4,4 @@ export { default as ReactMarkdownPlugin } from './react';
|
|
|
4
4
|
export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
|
|
5
5
|
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, } from './service/shortcut';
|
|
6
6
|
export { isPunctuationChar } from './utils';
|
|
7
|
+
export { detectCodeLanguage, detectLanguage } from './utils/detectLanguage';
|
|
@@ -2,4 +2,5 @@ export { INSERT_MARKDOWN_COMMAND } from "./command";
|
|
|
2
2
|
export { MarkdownPlugin } from "./plugin";
|
|
3
3
|
export { default as ReactMarkdownPlugin } from "./react";
|
|
4
4
|
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
|
|
5
|
-
export { isPunctuationChar } from "./utils";
|
|
5
|
+
export { isPunctuationChar } from "./utils";
|
|
6
|
+
export { detectCodeLanguage, detectLanguage } from "./utils/detectLanguage";
|
|
@@ -13,7 +13,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
|
|
|
13
13
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
14
14
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
15
15
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
16
|
-
import { $isCodeNode } from '@lexical/code';
|
|
16
|
+
import { $createCodeNode, $isCodeNode } from '@lexical/code';
|
|
17
17
|
import { $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, HISTORIC_TAG, KEY_ENTER_COMMAND, PASTE_COMMAND } from 'lexical';
|
|
18
18
|
import { KernelPlugin } from "../../../editor-kernel/plugin";
|
|
19
19
|
import { createDebugLogger } from "../../../utils/debug";
|
|
@@ -21,6 +21,7 @@ import { registerMarkdownCommand } from "../command";
|
|
|
21
21
|
import MarkdownDataSource from "../data-source/markdown-data-source";
|
|
22
22
|
import { IMarkdownShortCutService, MarkdownShortCutService } from "../service/shortcut";
|
|
23
23
|
import { canContainTransformableMarkdown } from "../utils";
|
|
24
|
+
import { detectCodeLanguage, detectLanguage } from "../utils/detectLanguage";
|
|
24
25
|
export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
25
26
|
_inherits(MarkdownPlugin, _KernelPlugin);
|
|
26
27
|
var _super = _createSuper(MarkdownPlugin);
|
|
@@ -136,43 +137,56 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
136
137
|
htmlLength: (html === null || html === void 0 ? void 0 : html.length) || 0,
|
|
137
138
|
textLength: text.length
|
|
138
139
|
});
|
|
140
|
+
var enablePasteMarkdown = (_this2$config$enableP = (_this2$config = _this2.config) === null || _this2$config === void 0 ? void 0 : _this2$config.enablePasteMarkdown) !== null && _this2$config$enableP !== void 0 ? _this2$config$enableP : true;
|
|
141
|
+
|
|
142
|
+
// If markdown formatting is disabled, we're done
|
|
143
|
+
if (!enablePasteMarkdown) {
|
|
144
|
+
// Force plain text paste for external content
|
|
145
|
+
event.preventDefault();
|
|
146
|
+
event.stopPropagation();
|
|
147
|
+
editor.update(function () {
|
|
148
|
+
var selection = $getSelection();
|
|
149
|
+
if (!$isRangeSelection(selection)) return;
|
|
150
|
+
|
|
151
|
+
// Insert plain text
|
|
152
|
+
selection.insertText(text);
|
|
153
|
+
});
|
|
154
|
+
_this2.logger.debug('markdown formatting disabled, plain text inserted');
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
139
157
|
|
|
140
158
|
// Check if this is likely a rich-text paste from the editor or other rich editor
|
|
141
159
|
// Rich text pastes typically have HTML that's more complex than just wrapping text
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Has block elements (p, div, h1-h6, etc.)
|
|
146
|
-
htmlDoc.querySelectorAll('p, div, h1, h2, h3, h4, h5, h6, ul, ol, li, table').length > 0 ||
|
|
147
|
-
// Has inline formatting (strong, em, code, etc.)
|
|
148
|
-
htmlDoc.querySelectorAll('strong, em, b, i, u, code, span[style]').length > 0 ||
|
|
149
|
-
// Has data attributes (often used by editors to store metadata)
|
|
150
|
-
htmlDoc.querySelectorAll('[data-lexical-text], [data-lexical-decorator]').length > 0;
|
|
151
|
-
if (hasRichContent) {
|
|
152
|
-
// This looks like rich content from an editor - let Lexical handle it
|
|
153
|
-
_this2.logger.debug('rich content detected, letting Lexical handle paste');
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
160
|
+
if (clipboardData.types.includes('application/x-lexical-editor')) {
|
|
161
|
+
_this2.logger.debug('rich content detected, letting Lexical handle paste');
|
|
162
|
+
return false;
|
|
156
163
|
}
|
|
157
164
|
|
|
158
165
|
// Check if markdown paste formatting is enabled (default: true)
|
|
159
|
-
var enablePasteMarkdown = (_this2$config$enableP = (_this2$config = _this2.config) === null || _this2$config === void 0 ? void 0 : _this2$config.enablePasteMarkdown) !== null && _this2$config$enableP !== void 0 ? _this2$config$enableP : true;
|
|
160
166
|
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
+
// Check if content is code (JSON, SQL, etc.) and should be inserted as code block
|
|
168
|
+
var codeInfo = _this2.detectCodeContent(text);
|
|
169
|
+
if (codeInfo) {
|
|
170
|
+
// Code detected - insert as code block
|
|
171
|
+
_this2.logger.debug("code detected (".concat(codeInfo.language, "), inserting as code block"));
|
|
172
|
+
event.preventDefault();
|
|
173
|
+
event.stopPropagation();
|
|
174
|
+
editor.update(function () {
|
|
175
|
+
var selection = $getSelection();
|
|
176
|
+
if (!$isRangeSelection(selection)) return;
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
// Create code block node with detected language
|
|
179
|
+
var codeNode = $createCodeNode(codeInfo.language);
|
|
180
|
+
selection.insertNodes([codeNode]);
|
|
171
181
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
// Insert the code text into the code block
|
|
183
|
+
codeNode.select();
|
|
184
|
+
var codeSelection = $getSelection();
|
|
185
|
+
if ($isRangeSelection(codeSelection)) {
|
|
186
|
+
codeSelection.insertText(text);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
return true; // Command handled
|
|
176
190
|
}
|
|
177
191
|
|
|
178
192
|
// Check if the pasted plain text contains markdown patterns
|
|
@@ -190,52 +204,87 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
190
204
|
// No markdown detected - plain text is already inserted
|
|
191
205
|
_this2.logger.debug('no markdown patterns detected, keeping as plain text');
|
|
192
206
|
}
|
|
207
|
+
if (clipboardData.types.includes('text/html') && clipboardData.types.includes('text/rtf')) {
|
|
208
|
+
// Code detected - insert as code block
|
|
209
|
+
_this2.logger.debug("code like, inserting as code block");
|
|
210
|
+
event.preventDefault();
|
|
211
|
+
event.stopPropagation();
|
|
212
|
+
editor.update(function () {
|
|
213
|
+
var selection = $getSelection();
|
|
214
|
+
if (!$isRangeSelection(selection)) return;
|
|
215
|
+
|
|
216
|
+
// Create code block node with detected language
|
|
217
|
+
var codeNode = $createCodeNode('plaintext');
|
|
218
|
+
selection.insertNodes([codeNode]);
|
|
219
|
+
|
|
220
|
+
// Insert the code text into the code block
|
|
221
|
+
codeNode.select();
|
|
222
|
+
var codeSelection = $getSelection();
|
|
223
|
+
if ($isRangeSelection(codeSelection)) {
|
|
224
|
+
codeSelection.insertText(text);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return true; // Command handled
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Force plain text paste for external content
|
|
231
|
+
event.preventDefault();
|
|
232
|
+
event.stopPropagation();
|
|
233
|
+
editor.update(function () {
|
|
234
|
+
var selection = $getSelection();
|
|
235
|
+
if (!$isRangeSelection(selection)) return;
|
|
236
|
+
|
|
237
|
+
// Insert plain text
|
|
238
|
+
selection.insertText(text);
|
|
239
|
+
});
|
|
193
240
|
return true; // Command handled
|
|
194
241
|
}, COMMAND_PRIORITY_CRITICAL));
|
|
195
242
|
this.register(registerMarkdownCommand(editor, this.service));
|
|
196
243
|
}
|
|
197
244
|
|
|
198
245
|
/**
|
|
199
|
-
* Detect if
|
|
200
|
-
*
|
|
246
|
+
* Detect if content is code and should be inserted as code block
|
|
247
|
+
* Uses advanced language detection with pattern matching
|
|
248
|
+
* Excludes markdown as it should be handled by markdown formatting dialog
|
|
201
249
|
*/
|
|
202
250
|
}, {
|
|
203
|
-
key: "
|
|
204
|
-
value: function
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
this.logger.debug('content is valid JSON, not treating as markdown');
|
|
212
|
-
return false;
|
|
213
|
-
} catch (_unused) {
|
|
214
|
-
// Not valid JSON, continue checking
|
|
251
|
+
key: "detectCodeContent",
|
|
252
|
+
value: function detectCodeContent(text) {
|
|
253
|
+
// Use the advanced language detector
|
|
254
|
+
var detected = detectLanguage(text);
|
|
255
|
+
if (detected && detected.confidence > 50) {
|
|
256
|
+
// Don't insert markdown as code block - it should trigger the formatting dialog
|
|
257
|
+
if (detected.language === 'markdown') {
|
|
258
|
+
return null;
|
|
215
259
|
}
|
|
260
|
+
this.logger.debug('language detected:', detected);
|
|
261
|
+
return detected;
|
|
216
262
|
}
|
|
217
263
|
|
|
218
|
-
//
|
|
219
|
-
var
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
264
|
+
// Fallback to fast detection for common formats
|
|
265
|
+
var fastDetected = detectCodeLanguage(text);
|
|
266
|
+
if (fastDetected) {
|
|
267
|
+
// Don't insert markdown as code block
|
|
268
|
+
if (fastDetected === 'markdown') {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
confidence: 80,
|
|
273
|
+
language: fastDetected
|
|
274
|
+
};
|
|
225
275
|
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
226
278
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
return pattern.test(text);
|
|
237
|
-
})) {
|
|
238
|
-
this.logger.debug('content looks like code, not treating as markdown');
|
|
279
|
+
/**
|
|
280
|
+
* Detect if text contains markdown patterns
|
|
281
|
+
* Returns false if content is likely code (will be handled by detectCodeContent)
|
|
282
|
+
*/
|
|
283
|
+
}, {
|
|
284
|
+
key: "detectMarkdownContent",
|
|
285
|
+
value: function detectMarkdownContent(text) {
|
|
286
|
+
// If code is detected, don't treat as markdown
|
|
287
|
+
if (this.detectCodeContent(text)) {
|
|
239
288
|
return false;
|
|
240
289
|
}
|
|
241
290
|
var markdownPatterns = [
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language detection utility for code snippets
|
|
3
|
+
* Uses pattern matching and heuristics to identify programming languages
|
|
4
|
+
*/
|
|
5
|
+
interface LanguageDetectionResult {
|
|
6
|
+
confidence: number;
|
|
7
|
+
language: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Detect the programming language of a code snippet
|
|
11
|
+
* @param code - The code snippet to analyze
|
|
12
|
+
* @returns Language detection result with confidence score
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectLanguage(code: string): LanguageDetectionResult | null;
|
|
15
|
+
/**
|
|
16
|
+
* Simple detection for common formats with high confidence
|
|
17
|
+
* Falls back to detectLanguage for more complex detection
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectCodeLanguage(code: string): string | null;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
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."); }
|
|
3
|
+
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); }
|
|
4
|
+
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; }
|
|
5
|
+
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; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
/**
|
|
8
|
+
* Language detection utility for code snippets
|
|
9
|
+
* Uses pattern matching and heuristics to identify programming languages
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
var LANGUAGE_PATTERNS = {
|
|
13
|
+
// Shell/Config
|
|
14
|
+
bash: {
|
|
15
|
+
optional: [/\|\||&&/, /echo\s+/, /if\s+\[/],
|
|
16
|
+
required: [/^#!\/bin\/(ba)?sh/, /\${?\w+}?/],
|
|
17
|
+
weight: 7
|
|
18
|
+
},
|
|
19
|
+
csharp: {
|
|
20
|
+
optional: [/\[.*]/, /async\s+Task/, /var\s+\w+\s*=/],
|
|
21
|
+
required: [/(public|private|protected)\s+(class|interface|namespace)/, /using\s+\w+/],
|
|
22
|
+
weight: 8
|
|
23
|
+
},
|
|
24
|
+
css: {
|
|
25
|
+
optional: [/@media|@import|@keyframes/, /!important/],
|
|
26
|
+
required: [/[#.]?[\w-]+\s*{/, /:\s*[\w#%-]+;/],
|
|
27
|
+
weight: 8
|
|
28
|
+
},
|
|
29
|
+
dockerfile: {
|
|
30
|
+
required: [/^FROM\s+/, /^(RUN|CMD|COPY|ADD|WORKDIR|ENV|EXPOSE)\s+/m],
|
|
31
|
+
weight: 10
|
|
32
|
+
},
|
|
33
|
+
go: {
|
|
34
|
+
optional: [/import\s+\(/, /:=/, /defer|goroutine/],
|
|
35
|
+
required: [/package\s+\w+/, /func\s+\w+/],
|
|
36
|
+
weight: 8
|
|
37
|
+
},
|
|
38
|
+
graphql: {
|
|
39
|
+
optional: [/fragment\s+/, /on\s+\w+/],
|
|
40
|
+
required: [/(query|mutation|subscription)\s+/, /{[\S\s]*}/],
|
|
41
|
+
weight: 8
|
|
42
|
+
},
|
|
43
|
+
// Web languages
|
|
44
|
+
html: {
|
|
45
|
+
optional: [/<\/\w+>/, /class=|id=/],
|
|
46
|
+
required: [/<(html|head|body|div|span|p|a|img|script|style|link|meta)/i],
|
|
47
|
+
weight: 9
|
|
48
|
+
},
|
|
49
|
+
ini: {
|
|
50
|
+
exclude: [/^\s*</, /{.*}/],
|
|
51
|
+
required: [/^\[.*]$/m, /\w+\s*=\s*/],
|
|
52
|
+
weight: 6
|
|
53
|
+
},
|
|
54
|
+
java: {
|
|
55
|
+
optional: [/@Override|@Autowired/, /extends|implements/, /System\.out/],
|
|
56
|
+
required: [/(public|private|protected)\s+(class|interface|static)/, /\w+\s+\w+\s*\([^)]*\)\s*{/],
|
|
57
|
+
weight: 8
|
|
58
|
+
},
|
|
59
|
+
// JavaScript family
|
|
60
|
+
javascript: {
|
|
61
|
+
exclude: [/^\s*</, /interface\s+\w+/, /:\s*\w+\s*[;=]/],
|
|
62
|
+
optional: [/=>\s*{/, /console\.(log|error|warn)/, /\.then\(/, /require\(/],
|
|
63
|
+
required: [/(function|const|let|var|class|import|export|async|await)\s/],
|
|
64
|
+
weight: 7
|
|
65
|
+
},
|
|
66
|
+
// Data formats
|
|
67
|
+
json: {
|
|
68
|
+
optional: [/"[^"]*"\s*:/, /:\s*["'[{]/],
|
|
69
|
+
required: [/^\s*[[{]/, /[\]}]\s*$/],
|
|
70
|
+
weight: 10
|
|
71
|
+
},
|
|
72
|
+
jsx: {
|
|
73
|
+
exclude: [/^\s*<!DOCTYPE/, /^\s*<html/],
|
|
74
|
+
optional: [/className=/, /{.*}/, /import.*from/],
|
|
75
|
+
required: [/<\w+[^>]*>/, /(const|function|class)\s+\w+/],
|
|
76
|
+
weight: 8
|
|
77
|
+
},
|
|
78
|
+
makefile: {
|
|
79
|
+
optional: [/\$\(.*\)/, /\.PHONY/],
|
|
80
|
+
required: [/^[\w-]+:\s*/, /^\t/m],
|
|
81
|
+
weight: 7
|
|
82
|
+
},
|
|
83
|
+
// Other common languages
|
|
84
|
+
markdown: {
|
|
85
|
+
optional: [/```/, /\*\*.*\*\*/, /^\s*[*+-]\s+/m],
|
|
86
|
+
required: [/^#{1,6}\s+/, /\[.*]\(.*\)/],
|
|
87
|
+
weight: 6
|
|
88
|
+
},
|
|
89
|
+
php: {
|
|
90
|
+
optional: [/function\s+\w+/, /class\s+\w+/, /echo|print/],
|
|
91
|
+
required: [/<\?php/, /\$\w+/],
|
|
92
|
+
weight: 10
|
|
93
|
+
},
|
|
94
|
+
powershell: {
|
|
95
|
+
optional: [/\|\s*Where/, /\|\s*Select/, /param\(/],
|
|
96
|
+
required: [/\$\w+/, /(Get|Set|New|Remove)-\w+/],
|
|
97
|
+
weight: 8
|
|
98
|
+
},
|
|
99
|
+
// Backend languages
|
|
100
|
+
python: {
|
|
101
|
+
optional: [/if __name__|print\(/, /self\.|lambda/, /@\w+\s*$/m],
|
|
102
|
+
required: [/(def|class|import|from)\s+\w+/, /:\s*$/m],
|
|
103
|
+
weight: 8
|
|
104
|
+
},
|
|
105
|
+
ruby: {
|
|
106
|
+
optional: [/do\s*\|.*\|/, /puts|require/, /:\w+\s*=>/],
|
|
107
|
+
required: [/(def|class|module|end)\s/, /@\w+/],
|
|
108
|
+
weight: 7
|
|
109
|
+
},
|
|
110
|
+
rust: {
|
|
111
|
+
optional: [/::\w+/, /&mut|&str/, /#\[derive\(/],
|
|
112
|
+
required: [/(fn|struct|impl|trait|use)\s+/, /let\s+(mut\s+)?\w+/],
|
|
113
|
+
weight: 8
|
|
114
|
+
},
|
|
115
|
+
scss: {
|
|
116
|
+
optional: [/@mixin|@include|@extend/, /#{.*}/],
|
|
117
|
+
required: [/\$[\w-]+\s*:/, /[&@]\w+/],
|
|
118
|
+
weight: 8
|
|
119
|
+
},
|
|
120
|
+
// Database
|
|
121
|
+
sql: {
|
|
122
|
+
optional: [/from|where|join|group by|order by/i, /\*/],
|
|
123
|
+
required: [/(select|insert|update|delete|create|alter|drop)\s+/i],
|
|
124
|
+
weight: 9
|
|
125
|
+
},
|
|
126
|
+
// Config formats
|
|
127
|
+
toml: {
|
|
128
|
+
optional: [/\[\[.*]]/, /"""[\S\s]*"""/],
|
|
129
|
+
required: [/^\[.*]$/m, /\w+\s*=\s*[\w"']/],
|
|
130
|
+
weight: 8
|
|
131
|
+
},
|
|
132
|
+
tsx: {
|
|
133
|
+
optional: [/:\s*React\./, /:\s*FC</, /useState|useEffect/],
|
|
134
|
+
required: [/<\w+[^>]*>/, /interface|type\s+\w+/],
|
|
135
|
+
weight: 9
|
|
136
|
+
},
|
|
137
|
+
typescript: {
|
|
138
|
+
optional: [/<\w+>/, /as\s+\w+/, /implements|extends/],
|
|
139
|
+
required: [/(interface|type|enum)\s+\w+/, /:\s*\w+(\[]|<.+>)?\s*[);=]/],
|
|
140
|
+
weight: 8
|
|
141
|
+
},
|
|
142
|
+
xml: {
|
|
143
|
+
optional: [/<\/\w+>/, /xmlns/],
|
|
144
|
+
required: [/<\?xml|<\w+[^>]*>/],
|
|
145
|
+
weight: 9
|
|
146
|
+
},
|
|
147
|
+
yaml: {
|
|
148
|
+
exclude: [/^\s*[[\]{]/],
|
|
149
|
+
required: [/^[\s-]*\w+:\s*/, /^[\s-]*-\s+/m],
|
|
150
|
+
weight: 8
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Detect the programming language of a code snippet
|
|
156
|
+
* @param code - The code snippet to analyze
|
|
157
|
+
* @returns Language detection result with confidence score
|
|
158
|
+
*/
|
|
159
|
+
export function detectLanguage(code) {
|
|
160
|
+
if (!code || code.trim().length === 0) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
var trimmed = code.trim();
|
|
164
|
+
var scores = {};
|
|
165
|
+
|
|
166
|
+
// Test each language pattern
|
|
167
|
+
for (var _i = 0, _Object$entries = Object.entries(LANGUAGE_PATTERNS); _i < _Object$entries.length; _i++) {
|
|
168
|
+
var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
|
|
169
|
+
language = _Object$entries$_i[0],
|
|
170
|
+
pattern = _Object$entries$_i[1];
|
|
171
|
+
var score = 0;
|
|
172
|
+
var baseWeight = pattern.weight || 5;
|
|
173
|
+
|
|
174
|
+
// Check exclude patterns first
|
|
175
|
+
if (pattern.exclude) {
|
|
176
|
+
var hasExclude = pattern.exclude.some(function (regex) {
|
|
177
|
+
return regex.test(trimmed);
|
|
178
|
+
});
|
|
179
|
+
if (hasExclude) {
|
|
180
|
+
continue; // Skip this language
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Check required patterns
|
|
185
|
+
if (pattern.required) {
|
|
186
|
+
var allRequired = pattern.required.every(function (regex) {
|
|
187
|
+
return regex.test(trimmed);
|
|
188
|
+
});
|
|
189
|
+
if (!allRequired) {
|
|
190
|
+
continue; // All required patterns must match
|
|
191
|
+
}
|
|
192
|
+
score += baseWeight * 2; // Strong indicator
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check optional patterns
|
|
196
|
+
if (pattern.optional) {
|
|
197
|
+
var matchedOptional = pattern.optional.filter(function (regex) {
|
|
198
|
+
return regex.test(trimmed);
|
|
199
|
+
}).length;
|
|
200
|
+
score += matchedOptional * baseWeight;
|
|
201
|
+
}
|
|
202
|
+
if (score > 0) {
|
|
203
|
+
scores[language] = score;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Find language with highest score
|
|
208
|
+
var entries = Object.entries(scores);
|
|
209
|
+
if (entries.length === 0) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
entries.sort(function (a, b) {
|
|
213
|
+
return b[1] - a[1];
|
|
214
|
+
});
|
|
215
|
+
var _entries$ = _slicedToArray(entries[0], 2),
|
|
216
|
+
topLanguage = _entries$[0],
|
|
217
|
+
topScore = _entries$[1];
|
|
218
|
+
|
|
219
|
+
// Calculate confidence (0-100)
|
|
220
|
+
var maxPossibleScore = (LANGUAGE_PATTERNS[topLanguage].weight || 5) * 5;
|
|
221
|
+
var confidence = Math.min(100, Math.round(topScore / maxPossibleScore * 100));
|
|
222
|
+
|
|
223
|
+
// Only return if confidence is high enough
|
|
224
|
+
if (confidence < 30) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
confidence: confidence,
|
|
229
|
+
language: topLanguage
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Simple detection for common formats with high confidence
|
|
235
|
+
* Falls back to detectLanguage for more complex detection
|
|
236
|
+
*/
|
|
237
|
+
export function detectCodeLanguage(code) {
|
|
238
|
+
var trimmed = code.trim();
|
|
239
|
+
|
|
240
|
+
// Fast path for JSON
|
|
241
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}') || trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
242
|
+
try {
|
|
243
|
+
JSON.parse(trimmed);
|
|
244
|
+
return 'json';
|
|
245
|
+
} catch (_unused) {
|
|
246
|
+
// Not valid JSON
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Fast path for common patterns
|
|
251
|
+
if (/^\s*(select|insert|update|delete|create|alter|drop)\s+/i.test(code)) {
|
|
252
|
+
return 'sql';
|
|
253
|
+
}
|
|
254
|
+
if (/^<\?xml/i.test(code)) {
|
|
255
|
+
return 'xml';
|
|
256
|
+
}
|
|
257
|
+
if (/^FROM\s+|^RUN\s+|^CMD\s+/m.test(code)) {
|
|
258
|
+
return 'dockerfile';
|
|
259
|
+
}
|
|
260
|
+
if (/<\?php/.test(code)) {
|
|
261
|
+
return 'php';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Use pattern matching for complex detection
|
|
265
|
+
var result = detectLanguage(code);
|
|
266
|
+
return result && result.confidence > 50 ? result.language : null;
|
|
267
|
+
}
|
package/package.json
CHANGED