@lobehub/editor 1.17.1 → 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);
|
|
@@ -130,32 +131,64 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
130
131
|
|
|
131
132
|
// If there's no text content, let Lexical handle it
|
|
132
133
|
if (!text) return false;
|
|
133
|
-
|
|
134
|
-
// Check if markdown paste formatting is enabled (default: true)
|
|
135
|
-
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;
|
|
136
134
|
_this2.logger.debug('paste content analysis:', {
|
|
137
|
-
|
|
135
|
+
clipboardTypes: Array.from(clipboardData.types || []),
|
|
138
136
|
hasHTML: !!(html && html.trim()),
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Always force plain text paste first (prevents HTML formatting issues)
|
|
143
|
-
event.preventDefault();
|
|
144
|
-
event.stopPropagation();
|
|
145
|
-
editor.update(function () {
|
|
146
|
-
var selection = $getSelection();
|
|
147
|
-
if (!$isRangeSelection(selection)) return;
|
|
148
|
-
|
|
149
|
-
// Insert plain text
|
|
150
|
-
selection.insertText(text);
|
|
137
|
+
htmlLength: (html === null || html === void 0 ? void 0 : html.length) || 0,
|
|
138
|
+
textLength: text.length
|
|
151
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;
|
|
152
141
|
|
|
153
142
|
// If markdown formatting is disabled, we're done
|
|
154
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
|
+
});
|
|
155
154
|
_this2.logger.debug('markdown formatting disabled, plain text inserted');
|
|
156
155
|
return true;
|
|
157
156
|
}
|
|
158
157
|
|
|
158
|
+
// Check if this is likely a rich-text paste from the editor or other rich editor
|
|
159
|
+
// Rich text pastes typically have HTML that's more complex than just wrapping text
|
|
160
|
+
if (clipboardData.types.includes('application/x-lexical-editor')) {
|
|
161
|
+
_this2.logger.debug('rich content detected, letting Lexical handle paste');
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Check if markdown paste formatting is enabled (default: true)
|
|
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;
|
|
177
|
+
|
|
178
|
+
// Create code block node with detected language
|
|
179
|
+
var codeNode = $createCodeNode(codeInfo.language);
|
|
180
|
+
selection.insertNodes([codeNode]);
|
|
181
|
+
|
|
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
|
|
190
|
+
}
|
|
191
|
+
|
|
159
192
|
// Check if the pasted plain text contains markdown patterns
|
|
160
193
|
var hasMarkdownContent = _this2.detectMarkdownContent(text);
|
|
161
194
|
if (hasMarkdownContent) {
|
|
@@ -171,52 +204,87 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
171
204
|
// No markdown detected - plain text is already inserted
|
|
172
205
|
_this2.logger.debug('no markdown patterns detected, keeping as plain text');
|
|
173
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
|
+
});
|
|
174
240
|
return true; // Command handled
|
|
175
241
|
}, COMMAND_PRIORITY_CRITICAL));
|
|
176
242
|
this.register(registerMarkdownCommand(editor, this.service));
|
|
177
243
|
}
|
|
178
244
|
|
|
179
245
|
/**
|
|
180
|
-
* Detect if
|
|
181
|
-
*
|
|
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
|
|
182
249
|
*/
|
|
183
250
|
}, {
|
|
184
|
-
key: "
|
|
185
|
-
value: function
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.logger.debug('content is valid JSON, not treating as markdown');
|
|
193
|
-
return false;
|
|
194
|
-
} catch (_unused) {
|
|
195
|
-
// 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;
|
|
196
259
|
}
|
|
260
|
+
this.logger.debug('language detected:', detected);
|
|
261
|
+
return detected;
|
|
197
262
|
}
|
|
198
263
|
|
|
199
|
-
//
|
|
200
|
-
var
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
+
};
|
|
206
275
|
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
207
278
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
return pattern.test(text);
|
|
218
|
-
})) {
|
|
219
|
-
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)) {
|
|
220
288
|
return false;
|
|
221
289
|
}
|
|
222
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