comparison third_party/highlight/es/languages/python.js @ 157:2db6253f355d

[ThirdParty] Added highlight library for better readability on blog.
author June Park <parkjune1995@gmail.com>
date Tue, 13 Jan 2026 19:18:47 -0800
parents
children
comparison
equal deleted inserted replaced
156:cd35e600ae34 157:2db6253f355d
1 /*! `python` grammar compiled for Highlight.js 11.11.1 */
2 var hljsGrammar = (function () {
3 'use strict';
4
5 /*
6 Language: Python
7 Description: Python is an interpreted, object-oriented, high-level programming language with dynamic semantics.
8 Website: https://www.python.org
9 Category: common
10 */
11
12 function python(hljs) {
13 const regex = hljs.regex;
14 const IDENT_RE = /[\p{XID_Start}_]\p{XID_Continue}*/u;
15 const RESERVED_WORDS = [
16 'and',
17 'as',
18 'assert',
19 'async',
20 'await',
21 'break',
22 'case',
23 'class',
24 'continue',
25 'def',
26 'del',
27 'elif',
28 'else',
29 'except',
30 'finally',
31 'for',
32 'from',
33 'global',
34 'if',
35 'import',
36 'in',
37 'is',
38 'lambda',
39 'match',
40 'nonlocal|10',
41 'not',
42 'or',
43 'pass',
44 'raise',
45 'return',
46 'try',
47 'while',
48 'with',
49 'yield'
50 ];
51
52 const BUILT_INS = [
53 '__import__',
54 'abs',
55 'all',
56 'any',
57 'ascii',
58 'bin',
59 'bool',
60 'breakpoint',
61 'bytearray',
62 'bytes',
63 'callable',
64 'chr',
65 'classmethod',
66 'compile',
67 'complex',
68 'delattr',
69 'dict',
70 'dir',
71 'divmod',
72 'enumerate',
73 'eval',
74 'exec',
75 'filter',
76 'float',
77 'format',
78 'frozenset',
79 'getattr',
80 'globals',
81 'hasattr',
82 'hash',
83 'help',
84 'hex',
85 'id',
86 'input',
87 'int',
88 'isinstance',
89 'issubclass',
90 'iter',
91 'len',
92 'list',
93 'locals',
94 'map',
95 'max',
96 'memoryview',
97 'min',
98 'next',
99 'object',
100 'oct',
101 'open',
102 'ord',
103 'pow',
104 'print',
105 'property',
106 'range',
107 'repr',
108 'reversed',
109 'round',
110 'set',
111 'setattr',
112 'slice',
113 'sorted',
114 'staticmethod',
115 'str',
116 'sum',
117 'super',
118 'tuple',
119 'type',
120 'vars',
121 'zip'
122 ];
123
124 const LITERALS = [
125 '__debug__',
126 'Ellipsis',
127 'False',
128 'None',
129 'NotImplemented',
130 'True'
131 ];
132
133 // https://docs.python.org/3/library/typing.html
134 // TODO: Could these be supplemented by a CamelCase matcher in certain
135 // contexts, leaving these remaining only for relevance hinting?
136 const TYPES = [
137 "Any",
138 "Callable",
139 "Coroutine",
140 "Dict",
141 "List",
142 "Literal",
143 "Generic",
144 "Optional",
145 "Sequence",
146 "Set",
147 "Tuple",
148 "Type",
149 "Union"
150 ];
151
152 const KEYWORDS = {
153 $pattern: /[A-Za-z]\w+|__\w+__/,
154 keyword: RESERVED_WORDS,
155 built_in: BUILT_INS,
156 literal: LITERALS,
157 type: TYPES
158 };
159
160 const PROMPT = {
161 className: 'meta',
162 begin: /^(>>>|\.\.\.) /
163 };
164
165 const SUBST = {
166 className: 'subst',
167 begin: /\{/,
168 end: /\}/,
169 keywords: KEYWORDS,
170 illegal: /#/
171 };
172
173 const LITERAL_BRACKET = {
174 begin: /\{\{/,
175 relevance: 0
176 };
177
178 const STRING = {
179 className: 'string',
180 contains: [ hljs.BACKSLASH_ESCAPE ],
181 variants: [
182 {
183 begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,
184 end: /'''/,
185 contains: [
186 hljs.BACKSLASH_ESCAPE,
187 PROMPT
188 ],
189 relevance: 10
190 },
191 {
192 begin: /([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,
193 end: /"""/,
194 contains: [
195 hljs.BACKSLASH_ESCAPE,
196 PROMPT
197 ],
198 relevance: 10
199 },
200 {
201 begin: /([fF][rR]|[rR][fF]|[fF])'''/,
202 end: /'''/,
203 contains: [
204 hljs.BACKSLASH_ESCAPE,
205 PROMPT,
206 LITERAL_BRACKET,
207 SUBST
208 ]
209 },
210 {
211 begin: /([fF][rR]|[rR][fF]|[fF])"""/,
212 end: /"""/,
213 contains: [
214 hljs.BACKSLASH_ESCAPE,
215 PROMPT,
216 LITERAL_BRACKET,
217 SUBST
218 ]
219 },
220 {
221 begin: /([uU]|[rR])'/,
222 end: /'/,
223 relevance: 10
224 },
225 {
226 begin: /([uU]|[rR])"/,
227 end: /"/,
228 relevance: 10
229 },
230 {
231 begin: /([bB]|[bB][rR]|[rR][bB])'/,
232 end: /'/
233 },
234 {
235 begin: /([bB]|[bB][rR]|[rR][bB])"/,
236 end: /"/
237 },
238 {
239 begin: /([fF][rR]|[rR][fF]|[fF])'/,
240 end: /'/,
241 contains: [
242 hljs.BACKSLASH_ESCAPE,
243 LITERAL_BRACKET,
244 SUBST
245 ]
246 },
247 {
248 begin: /([fF][rR]|[rR][fF]|[fF])"/,
249 end: /"/,
250 contains: [
251 hljs.BACKSLASH_ESCAPE,
252 LITERAL_BRACKET,
253 SUBST
254 ]
255 },
256 hljs.APOS_STRING_MODE,
257 hljs.QUOTE_STRING_MODE
258 ]
259 };
260
261 // https://docs.python.org/3.9/reference/lexical_analysis.html#numeric-literals
262 const digitpart = '[0-9](_?[0-9])*';
263 const pointfloat = `(\\b(${digitpart}))?\\.(${digitpart})|\\b(${digitpart})\\.`;
264 // Whitespace after a number (or any lexical token) is needed only if its absence
265 // would change the tokenization
266 // https://docs.python.org/3.9/reference/lexical_analysis.html#whitespace-between-tokens
267 // We deviate slightly, requiring a word boundary or a keyword
268 // to avoid accidentally recognizing *prefixes* (e.g., `0` in `0x41` or `08` or `0__1`)
269 const lookahead = `\\b|${RESERVED_WORDS.join('|')}`;
270 const NUMBER = {
271 className: 'number',
272 relevance: 0,
273 variants: [
274 // exponentfloat, pointfloat
275 // https://docs.python.org/3.9/reference/lexical_analysis.html#floating-point-literals
276 // optionally imaginary
277 // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
278 // Note: no leading \b because floats can start with a decimal point
279 // and we don't want to mishandle e.g. `fn(.5)`,
280 // no trailing \b for pointfloat because it can end with a decimal point
281 // and we don't want to mishandle e.g. `0..hex()`; this should be safe
282 // because both MUST contain a decimal point and so cannot be confused with
283 // the interior part of an identifier
284 {
285 begin: `(\\b(${digitpart})|(${pointfloat}))[eE][+-]?(${digitpart})[jJ]?(?=${lookahead})`
286 },
287 {
288 begin: `(${pointfloat})[jJ]?`
289 },
290
291 // decinteger, bininteger, octinteger, hexinteger
292 // https://docs.python.org/3.9/reference/lexical_analysis.html#integer-literals
293 // optionally "long" in Python 2
294 // https://docs.python.org/2.7/reference/lexical_analysis.html#integer-and-long-integer-literals
295 // decinteger is optionally imaginary
296 // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
297 {
298 begin: `\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?(?=${lookahead})`
299 },
300 {
301 begin: `\\b0[bB](_?[01])+[lL]?(?=${lookahead})`
302 },
303 {
304 begin: `\\b0[oO](_?[0-7])+[lL]?(?=${lookahead})`
305 },
306 {
307 begin: `\\b0[xX](_?[0-9a-fA-F])+[lL]?(?=${lookahead})`
308 },
309
310 // imagnumber (digitpart-based)
311 // https://docs.python.org/3.9/reference/lexical_analysis.html#imaginary-literals
312 {
313 begin: `\\b(${digitpart})[jJ](?=${lookahead})`
314 }
315 ]
316 };
317 const COMMENT_TYPE = {
318 className: "comment",
319 begin: regex.lookahead(/# type:/),
320 end: /$/,
321 keywords: KEYWORDS,
322 contains: [
323 { // prevent keywords from coloring `type`
324 begin: /# type:/
325 },
326 // comment within a datatype comment includes no keywords
327 {
328 begin: /#/,
329 end: /\b\B/,
330 endsWithParent: true
331 }
332 ]
333 };
334 const PARAMS = {
335 className: 'params',
336 variants: [
337 // Exclude params in functions without params
338 {
339 className: "",
340 begin: /\(\s*\)/,
341 skip: true
342 },
343 {
344 begin: /\(/,
345 end: /\)/,
346 excludeBegin: true,
347 excludeEnd: true,
348 keywords: KEYWORDS,
349 contains: [
350 'self',
351 PROMPT,
352 NUMBER,
353 STRING,
354 hljs.HASH_COMMENT_MODE
355 ]
356 }
357 ]
358 };
359 SUBST.contains = [
360 STRING,
361 NUMBER,
362 PROMPT
363 ];
364
365 return {
366 name: 'Python',
367 aliases: [
368 'py',
369 'gyp',
370 'ipython'
371 ],
372 unicodeRegex: true,
373 keywords: KEYWORDS,
374 illegal: /(<\/|\?)|=>/,
375 contains: [
376 PROMPT,
377 NUMBER,
378 {
379 // very common convention
380 scope: 'variable.language',
381 match: /\bself\b/
382 },
383 {
384 // eat "if" prior to string so that it won't accidentally be
385 // labeled as an f-string
386 beginKeywords: "if",
387 relevance: 0
388 },
389 { match: /\bor\b/, scope: "keyword" },
390 STRING,
391 COMMENT_TYPE,
392 hljs.HASH_COMMENT_MODE,
393 {
394 match: [
395 /\bdef/, /\s+/,
396 IDENT_RE,
397 ],
398 scope: {
399 1: "keyword",
400 3: "title.function"
401 },
402 contains: [ PARAMS ]
403 },
404 {
405 variants: [
406 {
407 match: [
408 /\bclass/, /\s+/,
409 IDENT_RE, /\s*/,
410 /\(\s*/, IDENT_RE,/\s*\)/
411 ],
412 },
413 {
414 match: [
415 /\bclass/, /\s+/,
416 IDENT_RE
417 ],
418 }
419 ],
420 scope: {
421 1: "keyword",
422 3: "title.class",
423 6: "title.class.inherited",
424 }
425 },
426 {
427 className: 'meta',
428 begin: /^[\t ]*@/,
429 end: /(?=#)|$/,
430 contains: [
431 NUMBER,
432 PARAMS,
433 STRING
434 ]
435 }
436 ]
437 };
438 }
439
440 return python;
441
442 })();
443 ;
444 export default hljsGrammar;