@@ -2,6 +2,8 @@ import * as vscode from "vscode";
22import { eolString } from "../utils/eol" ;
33import { DocInfo , linetype , Asmline } from "./scanDoc" ;
44
5+ type caseType = "upper" | "lower" | "title" | "off" ;
6+
57interface FormatConfig {
68 tab : boolean ;
79 tabSize : number ;
@@ -24,24 +26,31 @@ interface FormatConfig {
2426 * - `title`: `Mov` `Jmp`
2527 * - `off`: keep the original case
2628 */
27- instructionCase : "upper" | "lower" | "title" | "off" ;
29+ instructionCase : caseType ;
2830 /**
2931 * The case of registers
3032 */
31- registerCase : "upper" | "lower" | "title" | "off" ;
32-
33+ registerCase : caseType ;
3334 /**
3435 * The case of directives
3536 */
36- directiveCase : "upper" | "lower" | "title" | "off" ;
37+ directiveCase : caseType ;
38+ /**
39+ * The case of operators
40+ */
41+ operatorCase : caseType ;
3742 /**
3843 * Whether to align the operands
3944 */
4045 alignOperand : boolean ;
4146 /**
4247 * Whether to align the comments
4348 */
44- alignComment : boolean ;
49+ alignTrailingComment : boolean ;
50+ /**
51+ * Whether to align the single line comments
52+ */
53+ alignSingleLineComment : boolean ;
4554 /**
4655 * Whether to add a space after the comma
4756 */
@@ -61,12 +70,14 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
6170 tab : ! options . insertSpaces ,
6271 tabSize : options . tabSize ,
6372 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" ,
7081 } ;
7182 const textedits : vscode . TextEdit [ ] = [ ] ;
7283 const docinfo = DocInfo . getDocInfo ( document ) ;
@@ -83,6 +94,7 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
8394 else {
8495 newText = align ( docinfo . lines , item , config ) ;
8596 }
97+ postFormat ( newText , config ) ;
8698 const range = document . validateRange ( item . range ) ;
8799 textedits . push (
88100 new vscode . TextEdit ( range , newText . join ( eolString ( document . eol ) ) )
@@ -93,6 +105,34 @@ export class AsmDocFormat implements vscode.DocumentFormattingEditProvider {
93105 }
94106}
95107
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+
96136/**
97137 * Recursively align the code in a node
98138 * @param lines
@@ -257,22 +297,30 @@ function formatLine(
257297 const isVariable = line . type === linetype . variable ;
258298 if ( isLabel || isVariable ) {
259299 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 ) ;
266305 }
267306 else if ( line . type === linetype . onlycomment ) {
268307 output . push ( indentStr ( config ) + line . comment ) ;
269308 }
270309 else if ( line . main ) {
271310 let str = line . main . replace ( / \s + / , " " ) ;
311+
272312 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 ;
276324 }
277325 output . push ( str ) ;
278326 }
@@ -308,12 +356,36 @@ function formatLabelLine(
308356 }
309357 str += line . name ? space ( indent ) : indentStr ( config , indent ) ;
310358 }
311- str += line . operator ;
359+ str += convertCase ( line . operator ?? '' , config . instructionCase ) ;
312360 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 ;
314376 }
315377 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 ;
317389 }
318390 return str ;
319391}
@@ -339,4 +411,65 @@ function indentStr(config: { tab: boolean, tabSize: number }, size?: number) {
339411 const indenter = tab ? "\t" : space ( tabSize ) ;
340412 return indenter . repeat ( Math . floor ( size / tabSize ) ) + // initial indent
341413 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+ }
342475}
0 commit comments