diff --git a/lib/languages.ts b/lib/languages.ts index 0e9d5e4318c..93eb33dba2b 100644 --- a/lib/languages.ts +++ b/lib/languages.ts @@ -38,7 +38,8 @@ type DefKeys = | 'logoUrl' | 'logoUrlDark' | 'monacoDisassembly' - | 'tooltip'; + | 'tooltip' + | 'digitSeparator'; type LanguageDefinition = Pick; const definitions: Record = { @@ -63,6 +64,7 @@ const definitions: Record = { formatter: 'clangformat', previewFilter: /^\s*#include/, monacoDisassembly: null, + digitSeparator: "'", }, ada: { name: 'Ada', @@ -97,6 +99,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, 'android-kotlin': { name: 'Android Kotlin', @@ -108,6 +111,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, assembly: { name: 'Assembly', @@ -130,6 +134,7 @@ const definitions: Record = { formatter: 'clangformat', previewFilter: /^\s*#include/, monacoDisassembly: null, + digitSeparator: "'", }, c3: { name: 'C3', @@ -163,6 +168,7 @@ const definitions: Record = { logoUrlDark: null, formatter: null, monacoDisassembly: null, + digitSeparator: "'", }, circt: { name: 'CIRCT', @@ -229,6 +235,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: "'", }, mlir: { name: 'MLIR', @@ -251,6 +258,7 @@ const definitions: Record = { formatter: null, previewFilter: /^\s*#include/, monacoDisassembly: null, + digitSeparator: "'", }, cppx_blue: { name: 'Cppx-Blue', @@ -273,6 +281,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: "'", }, cpp2_cppfront: { name: 'Cpp2-cppfront', @@ -284,6 +293,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: 'cppp', + digitSeparator: "'", }, crystal: { name: 'Crystal', @@ -295,6 +305,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, csharp: { name: 'C#', @@ -306,6 +317,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, cuda: { name: 'CUDA C++', @@ -317,6 +329,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: "'", }, d: { name: 'D', @@ -394,6 +407,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, haskell: { name: 'Haskell', @@ -405,6 +419,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, hlsl: { name: 'HLSL', @@ -460,6 +475,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, julia: { name: 'Julia', @@ -471,6 +487,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, kotlin: { name: 'Kotlin', @@ -482,6 +499,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, llvm: { name: 'LLVM IR', @@ -548,6 +566,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: "'", }, ocaml: { name: 'OCaml', @@ -603,6 +622,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, racket: { name: 'Racket', @@ -625,6 +645,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: 'asmruby', + digitSeparator: '_', }, rust: { name: 'Rust', @@ -636,6 +657,7 @@ const definitions: Record = { formatter: 'rustfmt', previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, snowball: { name: 'Snowball', @@ -658,6 +680,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, solidity: { name: 'Solidity', @@ -691,6 +714,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, tablegen: { name: 'LLVM TableGen', @@ -724,6 +748,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, v: { name: 'V', @@ -768,6 +793,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, javascript: { name: 'Javascript', @@ -779,6 +805,7 @@ const definitions: Record = { formatter: null, previewFilter: null, monacoDisassembly: null, + digitSeparator: '_', }, gimple: { name: 'GIMPLE', diff --git a/shared/common-utils.ts b/shared/common-utils.ts index 9a2481c0918..f339b6082c5 100644 --- a/shared/common-utils.ts +++ b/shared/common-utils.ts @@ -76,3 +76,26 @@ const EscapeRE = new RegExp(`(?:${Object.keys(EscapeMap).join('|')})`, 'g'); export function escapeHTML(text: string) { return text.replace(EscapeRE, str => EscapeMap[str]); } + +function splitIntoChunks(s: string, chunkSize: number): string[] { + const chunks: string[] = []; + const isNegative = s.slice(0, 1) === '-'; + if (isNegative) { + s = s.slice(1); + } + const firstChunkLength = s.length % chunkSize; + if (firstChunkLength !== 0) { + chunks.push(s.slice(0, firstChunkLength)); + } + for (let i = firstChunkLength; i < s.length; i += chunkSize) { + chunks.push(s.slice(i, i + chunkSize)); + } + if (isNegative) { + chunks[0] = '-' + (chunks[0] ?? ''); + } + return chunks; +} + +export function addDigitSeparator(n: string, digitSeparator: string, chunkSize: number): string { + return splitIntoChunks(n, chunkSize).join(digitSeparator); +} diff --git a/static/panes/compiler.ts b/static/panes/compiler.ts index ad7d2b37802..9513f46a3a6 100644 --- a/static/panes/compiler.ts +++ b/static/panes/compiler.ts @@ -80,7 +80,7 @@ import {CompilerShared} from '../compiler-shared.js'; import {SentryCapture} from '../sentry.js'; import {LLVMIrBackendOptions} from '../compilation/ir.interfaces.js'; import {InstructionSet} from '../instructionsets.js'; -import {escapeHTML} from '../../shared/common-utils.js'; +import {addDigitSeparator, escapeHTML} from '../../shared/common-utils.js'; import {CompilerVersionInfo, setCompilerVersionPopoverForPane} from '../widgets/compiler-version-info.js'; const toolIcons = require.context('../../views/resources/logos', false, /\.(png|svg)$/); @@ -3496,7 +3496,15 @@ export class Compiler extends MonacoPane { + const numberString = number.toString(base).toUpperCase(); + if (digitSeparator !== undefined) { + return addDigitSeparator(numberString, digitSeparator, chunkSize); + } else { + return numberString; + } + }; const numericValue = this.parseNumericValue(value); if (numericValue === null) return null; @@ -3507,14 +3515,14 @@ export class Compiler extends MonacoPane { it('should prevent basic injection', () => { @@ -32,3 +32,24 @@ describe('HTML Escape Test Cases', () => { escapeHTML('\'"`>').should.equal(`'"`>`); }); }); + +describe('digit separator', () => { + it('handles short numbers', () => { + addDigitSeparator('42', '_', 3).should.equal('42'); + }); + it('handles long numbers', () => { + addDigitSeparator('1234', '_', 3).should.equal('1_234'); + addDigitSeparator('123456789', "'", 3).should.equal("123'456'789"); + addDigitSeparator('1234567890', "'", 3).should.equal("1'234'567'890"); + }); + it('handles hex numbers', () => { + addDigitSeparator('AABBCCDD12345678', '_', 4).should.equal('AABB_CCDD_1234_5678'); + addDigitSeparator('01AABBCCDD12345678', '_', 4).should.equal('01_AABB_CCDD_1234_5678'); + }); + it('handles negative numbers', () => { + addDigitSeparator('-42', '_', 3).should.equal('-42'); + addDigitSeparator('-420', '_', 3).should.equal('-420'); + addDigitSeparator('-4200', '_', 3).should.equal('-4_200'); + addDigitSeparator('-123456789', '_', 3).should.equal('-123_456_789'); + }); +}); diff --git a/types/languages.interfaces.ts b/types/languages.interfaces.ts index 8bdd26b56d9..a50a81b368f 100644 --- a/types/languages.interfaces.ts +++ b/types/languages.interfaces.ts @@ -124,4 +124,5 @@ export interface Language { tooltip?: string; /** Default compiler for the language. This is populated when handed to the frontend. */ defaultCompiler?: string; + digitSeparator?: string; }