@@ -2,6 +2,8 @@ import * as vscode from "vscode";
2
2
import { eolString } from "../utils/eol" ;
3
3
import { DocInfo , linetype , Asmline } from "./scanDoc" ;
4
4
5
+ type caseType = "upper" | "lower" | "title" | "off" ;
6
+
5
7
interface FormatConfig {
6
8
tab : boolean ;
7
9
tabSize : number ;
@@ -24,24 +26,31 @@ interface FormatConfig {
24
26
* - `title`: `Mov` `Jmp`
25
27
* - `off`: keep the original case
26
28
*/
27
- instructionCase : "upper" | "lower" | "title" | "off" ;
29
+ instructionCase : caseType ;
28
30
/**
29
31
* The case of registers
30
32
*/
31
- registerCase : "upper" | "lower" | "title" | "off" ;
32
-
33
+ registerCase : caseType ;
33
34
/**
34
35
* The case of directives
35
36
*/
36
- directiveCase : "upper" | "lower" | "title" | "off" ;
37
+ directiveCase : caseType ;
38
+ /**
39
+ * The case of operators
40
+ */
41
+ operatorCase : caseType ;
37
42
/**
38
43
* Whether to align the operands
39
44
*/
40
45
alignOperand : boolean ;
41
46
/**
42
47
* Whether to align the comments
43
48
*/
44
- alignComment : boolean ;
49
+ alignTrailingComment : boolean ;
50
+ /**
51
+ * Whether to align the single line comments
52
+ */
53
+ alignSingleLineComment : boolean ;
45
54
/**
46
55
* Whether to add a space after the comma
47
56
*/
@@ -61,12 +70,14 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
61
70
tab : ! options . insertSpaces ,
62
71
tabSize : options . tabSize ,
63
72
align : "label" ,
64
- instructionCase : "off" ,
65
- registerCase : "off" ,
66
- directiveCase : "off" ,
67
- alignOperand : false ,
68
- alignComment : false ,
69
- spaceAfterComma : "off" ,
73
+ instructionCase : "title" ,
74
+ registerCase : "upper" ,
75
+ directiveCase : "lower" ,
76
+ operatorCase : "lower" ,
77
+ alignOperand : true ,
78
+ alignTrailingComment : true ,
79
+ alignSingleLineComment : true ,
80
+ spaceAfterComma : "always" ,
70
81
} ;
71
82
const textedits : vscode . TextEdit [ ] = [ ] ;
72
83
const docinfo = DocInfo . getDocInfo ( document ) ;
@@ -83,6 +94,7 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
83
94
else {
84
95
newText = align ( docinfo . lines , item , config ) ;
85
96
}
97
+ postFormat ( newText , config ) ;
86
98
const range = document . validateRange ( item . range ) ;
87
99
textedits . push (
88
100
new vscode . TextEdit ( range , newText . join ( eolString ( document . eol ) ) )
@@ -93,6 +105,34 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
93
105
}
94
106
}
95
107
108
+ function postFormat ( text : string [ ] , config : FormatConfig ) {
109
+ for ( let i = text . length - 1 ; i >= 0 ; i -- ) {
110
+ // test if current line is a a comment only line, use regexp
111
+ let line = text [ i ] ;
112
+ // Remove the trailing spaces
113
+ text [ i ] = text [ i ] . trimEnd ( ) ;
114
+ if ( / ^ \s * ; / . test ( line ) ) {
115
+ // Align the single line comment
116
+ if ( config . alignSingleLineComment && i !== text . length ) {
117
+ line = line . trimStart ( ) ;
118
+ // align the single line comment to the next line, if next line is not empty
119
+ const nextLine = text [ i + 1 ] ;
120
+ if ( nextLine && nextLine !== "" ) {
121
+ const match = nextLine . match ( / ^ \s * / ) ;
122
+ if ( match ) {
123
+ text [ i ] = match [ 0 ] + line ;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ else if ( config . directiveCase !== 'off' ) {
129
+ // Convert the case of directives
130
+ text [ i ] = convertDirectiveCase ( text [ i ] , config . directiveCase ) ;
131
+ }
132
+ }
133
+ return text ;
134
+ }
135
+
96
136
/**
97
137
* Recursively align the code in a node
98
138
* @param lines
@@ -257,22 +297,30 @@ function formatLine(
257
297
const isVariable = line . type === linetype . variable ;
258
298
if ( isLabel || isVariable ) {
259
299
const alignSize = config . align === 'indent' && isLabel ? config . tabSize - 1 : size . name ;
260
- output . push (
261
- formatLabelLine ( line , alignOpt , {
262
- ...size ,
263
- name : alignSize ,
264
- } , config )
265
- ) ;
300
+ const str = formatLabelLine ( line , alignOpt , {
301
+ ...size ,
302
+ name : alignSize ,
303
+ } , config ) ;
304
+ output . push ( str ) ;
266
305
}
267
306
else if ( line . type === linetype . onlycomment ) {
268
307
output . push ( indentStr ( config ) + line . comment ) ;
269
308
}
270
309
else if ( line . main ) {
271
310
let str = line . main . replace ( / \s + / , " " ) ;
311
+
272
312
if ( line . comment ) {
273
- //后补充空格
274
- str += space ( size . name + 1 + size . operator + 1 + size . operand - str . length ) ;
275
- str += indentStr ( config , config . tabSize * 2 ) + line . comment ;
313
+ if ( config . alignTrailingComment ) {
314
+ //后补充空格
315
+ str += space ( size . name + 1 + size . operator + 1 + size . operand - str . length ) + indentStr ( config , config . tabSize * 2 ) ;
316
+ }
317
+ else {
318
+ const match = line . str . match ( / \s * (? = ; ) / ) ;
319
+ if ( match ) {
320
+ str += match [ 0 ] ;
321
+ }
322
+ }
323
+ str += line . comment ;
276
324
}
277
325
output . push ( str ) ;
278
326
}
@@ -308,12 +356,36 @@ function formatLabelLine(
308
356
}
309
357
str += line . name ? space ( indent ) : indentStr ( config , indent ) ;
310
358
}
311
- str += line . operator ;
359
+ str += convertCase ( line . operator ?? '' , config . instructionCase ) ;
312
360
if ( line . operand || line . comment ) { //操作码后补充空格
313
- str += `${ space ( size . operator - operatorLength ) } ${ line . operand } ` ;
361
+ if ( config . alignOperand ) {
362
+ str += space ( size . operator - operatorLength ) ;
363
+ }
364
+ str += ' ' ;
365
+ let operand = line . operand ?? '' ;
366
+ if ( config . registerCase !== 'off' && line . operand && isLabel ) {
367
+ operand = convertRegisterCase ( operand , config . registerCase ) ;
368
+ }
369
+ if ( config . operatorCase !== 'off' && line . operand ) {
370
+ operand = convertOperatorCase ( operand , config . operatorCase ) ;
371
+ }
372
+ if ( config . spaceAfterComma !== 'off' && line . operand ) {
373
+ operand = adjustSpaceAfterComma ( operand , config . spaceAfterComma === 'always' ) ;
374
+ }
375
+ str += operand ;
314
376
}
315
377
if ( line . comment ) { //操作数后补充空格
316
- str += `${ space ( size . operand - operandLength ) } ${ indentStr ( config ) } ${ line . comment } ` ;
378
+ if ( config . alignTrailingComment ) {
379
+ str += `${ space ( size . operand - operandLength ) } ${ indentStr ( config ) } ` ;
380
+ }
381
+ else {
382
+ // get the original \s before `;` in line.str
383
+ const match = line . str . match ( / \s * (? = ; ) / ) ;
384
+ if ( match ) {
385
+ str += match [ 0 ] ;
386
+ }
387
+ }
388
+ str += line . comment ;
317
389
}
318
390
return str ;
319
391
}
@@ -339,4 +411,65 @@ function indentStr(config: { tab: boolean, tabSize: number }, size?: number) {
339
411
const indenter = tab ? "\t" : space ( tabSize ) ;
340
412
return indenter . repeat ( Math . floor ( size / tabSize ) ) + // initial indent
341
413
space ( size % tabSize ) ; // align indent
414
+ }
415
+
416
+ function convertCase ( word : string , toCase : caseType ) {
417
+ switch ( toCase ) {
418
+ case 'upper' :
419
+ return word . toUpperCase ( ) ;
420
+ case 'lower' :
421
+ return word . toLowerCase ( ) ;
422
+ case 'title' :
423
+ if ( word . length === 0 ) {
424
+ return word ;
425
+ }
426
+ // Find the first letter
427
+ const firstIndex = word . search ( / [ a - z A - Z ] / ) ;
428
+ if ( firstIndex === - 1 ) {
429
+ return word ;
430
+ }
431
+ const first = word [ firstIndex ] ;
432
+ const rest = word . slice ( firstIndex + 1 ) ;
433
+ return word . slice ( 0 , firstIndex ) + first . toUpperCase ( ) + rest . toLowerCase ( ) ;
434
+ default :
435
+ return word ;
436
+ }
437
+ }
438
+
439
+ function convertCaseFor ( str : string , toCase : caseType , regex : RegExp ) {
440
+ return str . replace ( regex , ( match ) => {
441
+ if ( ! match ) {
442
+ return match ;
443
+ }
444
+ return convertCase ( match , toCase ) ;
445
+ } ) ;
446
+ }
447
+
448
+
449
+ function convertRegisterCase ( str : string , toCase : caseType ) {
450
+ const regex = / (?< ! ; .* ?) \b ( (?< general > E A X | E B X | E C X | E D X | A X | B X | C X | D X | A L | A H | B L | B H | C L | C H | D L | D H ) | (?< segment > C S | D S | E S | F S | G S | S S ) | (?< pointer > D I | S I | B P | S P | I P ) | (?< control > C R [ 0 1 2 3 4 ] ) | (?< ProtectedMode > G D T R | I D T R | L D T R | T R ) | (?< DebugTest > D R [ 0 - 7 ] | T R [ 3 - 7 ] ) | (?< float > R [ 0 - 7 ] ) ) \b (? = (?: [ ^ ' " ] | ' [ ^ ' ] * ' | " [ ^ " ] * " ) * $ ) / gi;
451
+
452
+ return convertCaseFor ( str , toCase , regex ) ;
453
+ }
454
+
455
+ function convertDirectiveCase ( str : string , toCase : caseType ) {
456
+ const regex = / (?< ! ; .* ?) (?< ! \S ) ( (?< x64 > \. A L L O C S T A C K | \. E N D P R O L O G | P R O C | \. P U S H F R A M E | \. P U S H R E G | \. S A V E R E G | \. S A V E X M M 1 2 8 | \. S E T F R A M E ) | (?< CodeLabels > A L I G N | E V E N | L A B E L | O R G ) | (?< ConditionalAssembly > E L S E | E L S E I F | E L S E I F 2 | I F | I F 2 | I F B | I F N B | I F D E F | I F N D E F | I F D I F | I F D I F I | I F E | I F I D N | I F I D N I ) | (?< ConditionalControlFlow > \. B R E A K | \. C O N T I N U E | \. E L S E | \. E L S E I F | \. E N D I F | \. E N D W | \. I F | \. R E P E A T | \. U N T I L | \. U N T I L C X Z | \. W H I L E ) | (?< ConditionalError > \. E R R | \. E R R 2 | \. E R R B | \. E R R D E F | \. E R R D I F | \. E R R D I F I | \. E R R E | \. E R R I D N | \. E R R I D N I | \. E R R N B | \. E R R N D E F | \. E R R N Z ) | (?< DataAllocation > D B | D W | D D | D Q | D F | D T | A L I G N | B Y T E | S B Y T E | D W O R D | S D W O R D | E V E N | F W O R D | L A B E L | O R G | Q W O R D | R E A L 4 | R E A L 8 | R E A L 1 0 | T B Y T E | W O R D | S W O R D ) | (?< Equates > = | E Q U | T E X T E Q U ) | (?< ListingControl > \. C R E F | \. L I S T | \. L I S T A L L | \. L I S T I F | \. L I S T M A C R O | \. L I S T M A C R O A L L | \. N O C R E F | \. N O L I S T | \. N O L I S T I F | \. N O L I S T M A C R O | P A G E | S U B T I T L E | \. T F C O N D | T I T L E ) | (?< Macros > E N D M | E X I T M | G O T O | L O C A L | M A C R O | P U R G E ) | (?< Miscellaneous > A L I A S | A S S U M E | C O M M E N T | E C H O | E N D | \. F P O | I N C L U D E | I N C L U D E L I B | M M W O R D | O P T I O N | P O P C O N T E X T | P U S H C O N T E X T | \. R A D I X | \. S A F E S E H | X M M W O R D | Y M M W O R D ) | (?< Procedures > E N D P | I N V O K E | P R O C | P R O T O ) | (?< Processor > \. 3 8 6 | \. 3 8 6 P | \. 3 8 7 | \. 4 8 6 | \. 4 8 6 P | \. 5 8 6 | \. 5 8 6 P | \. 6 8 6 | \. 6 8 6 P | \. K 3 D | \. M M X | \. X M M ) | (?< RepeatBlocks > E N D M | F O R | F O R C | G O T O | R E P E A T | W H I L E ) | (?< Scope > C O M M | E X T E R N | E X T E R N D E F | I N C L U D E L I B | P U B L I C ) | (?< Segment > \. A L P H A | A S S U M E | \. D O S S E G | E N D | E N D S | G R O U P | S E G M E N T | \. S E Q ) | (?< SimplifiedSegment > \. C O D E | \. C O N S T | \. D A T A | \. D A T A \? | \. D O S S E G | \. E X I T | \. F A R D A T A | \. F A R D A T A \? | \. M O D E L | \. S T A C K | \. S T A R T U P ) | (?< String > C A T S T R | I N S T R | S I Z E S T R | S U B S T R ) | (?< StructureAndRecord > E N D S | R E C O R D | S T R U C T | T Y P E D E F | U N I O N ) ) \b (? = (?: [ ^ ' " ] | ' [ ^ ' ] * ' | " [ ^ " ] * " ) * $ ) / gi;
457
+
458
+ return convertCaseFor ( str , toCase , regex ) ;
459
+ }
460
+
461
+ function convertOperatorCase ( str : string , toCase : caseType ) {
462
+ const regex = / (?< ! ; .* ?) (?< ! \S ) ( A B S | A D D R | A N D | D U P | R E P | E Q | G E | G T | H I G H | H I G H 3 2 | H I G H W O R D | I M A G E R E L | L E | L E N G T H | L E N G T H O F | L O W | L O W 3 2 | L O W W O R D | L R O F F S E T | L T | M A S K | M O D | N E | N O T | O F F S E T | O P A T T R | O R | P T R | S E G | S H L | .T Y P E | S E C T I O N R E L | S H O R T | S H R | S I Z E | S I Z E O F | T H I S | T Y P E | W I D T H | X O R ) \b (? = (?: [ ^ ' " ] | ' [ ^ ' ] * ' | " [ ^ " ] * " ) * $ ) / gi;
463
+
464
+ return convertCaseFor ( str , toCase , regex ) ;
465
+ }
466
+
467
+ function adjustSpaceAfterComma ( str : string , space : boolean ) {
468
+ const regex = / (?< ! ; .* ?) ( \s * ) , ( \s * ) (? = (?: [ ^ ' " ] | ' [ ^ ' ] * ' | " [ ^ " ] * " ) * $ ) / gi;
469
+ if ( space ) {
470
+ return str . replace ( regex , ', ' ) ;
471
+ }
472
+ else {
473
+ return str . replace ( regex , ',' ) ;
474
+ }
342
475
}
0 commit comments