1+ // Core
2+ import { Range , TextEdit } from 'vscode-languageserver' ;
3+ import { TextDocument } from 'vscode-languageserver-textdocument' ;
4+ import { VbaFmtListener } from './parser/vbaListener' ;
5+
6+
7+ export function getFormattingEdits ( document : TextDocument , listener : VbaFmtListener , range ?: Range ) : TextEdit [ ] {
8+ // Return nothing if we have nothing.
9+ if ( document . getText ( ) == '' )
10+ return [ ] ;
11+
12+ const result : TextEdit [ ] = [ ] ;
13+
14+ const startLine = range ?. start . line ?? 0 ;
15+ const endLine = ( range ?. end . line ?? document . lineCount ) + 1 ;
16+
17+ let trackedIndentLevel = 0 ;
18+ const baseIndentLevel = getIndentLevel ( document . getText ( range ) ) ;
19+
20+ for ( let i = startLine ; i < endLine ; i ++ ) {
21+ const text = getLine ( document , i ) ;
22+
23+ // Ignore comment lines.
24+ if ( / ^ \s * ' / . test ( text ) ) continue ;
25+
26+ // Actual indent level
27+ const currentIndentLevel = getIndentLevel ( text ) ;
28+ const newIndentLevel = listener . getIndent ( i ) ;
29+ if ( currentIndentLevel != newIndentLevel ) {
30+ result . push ( {
31+ range : getIndentRange ( text , i ) ! ,
32+ newText : ' ' . repeat ( newIndentLevel * 2 )
33+ } ) ;
34+ }
35+ }
36+
37+ return result ;
38+ }
39+
40+ function getExpectedIndent ( listener : VbaFmtListener , range : Range , n : number ) {
41+ // The listener will be offset by the range, e.g., if the range.start.line
42+ // is 5 then getting line 2 from the listener will be document line 7.
43+ return listener . getIndent ( n - range . start . line + 1 )
44+ }
45+
46+ function getIndentRange ( text : string , n : number ) : Range | undefined {
47+ const match = / ^ (? ! \s * ' ) ( \s * ) / m. exec ( text ) ;
48+ if ( match ) {
49+ return {
50+ start : { line : n , character : 0 } ,
51+ end : { line : n , character : match [ 0 ] . length }
52+ }
53+ }
54+ }
55+
56+ function getIndentLevel ( text : string ) : number {
57+ // Get spaces at start of non-comment lines (tab is four spaces)
58+ const normalised = text . replace ( / \t / g, ' ' )
59+ const match = / ^ (? ! \s * ' ) ( \s * ) / m. exec ( normalised ) ;
60+
61+ // Default is no indent.
62+ if ( ! match ) {
63+ return 0 ;
64+ }
65+
66+ // Four spaces per indent.
67+ return ( match [ 0 ] . length / 4 ) | 0 ;
68+ }
69+
70+ function getLine ( d : TextDocument , n : number ) : string {
71+ return d . getText ( {
72+ start : { line : n , character : 0 } ,
73+ end : { line : n + 1 , character : 0
74+ }
75+ } )
76+ }
0 commit comments