@@ -11,4 +11,168 @@ export const retrieveNodesAtPosition = async (positionOffset: number, plugin: Pl
11
11
}
12
12
}
13
13
return { nodesAtPosition, block }
14
- }
14
+ }
15
+
16
+ export const extractFunctionComments = ( code : string , indentSize : number = 0 , isSolidity : boolean = false ) => {
17
+ // Different patterns for Solidity vs non-Solidity functions
18
+ const functionPattern = isSolidity
19
+ ? / (?: f u n c t i o n \s + ( \w + ) | c o n s t r u c t o r ) \s * (?: \( [ ^ ) ] * \) ) ? \s * (?: p u b l i c | p r i v a t e | i n t e r n a l | e x t e r n a l ) ? \s * (?: v i e w | p u r e | p a y a b l e ) ? \s * (?: r e t u r n s \s * \( [ ^ ) ] * \) ) ? \s * (?: i s \w + ) ? \s * { / g
20
+ : / (?: f u n c t i o n \s + ( \w + ) | c o n s t r u c t o r | ( \w + ) \s * \( [ ^ ) ] * \) \s * (?: : \s * \w + (?: < [ ^ > ] + > ) ? ) ? \s * { ) / g
21
+
22
+ const functionPositions = new Map < string , { position : number , commentStart ?: number , singleLineComments ?: string [ ] } > ( )
23
+ let functionMatch
24
+
25
+ // First pass: find all function positions
26
+ while ( ( functionMatch = functionPattern . exec ( code ) ) !== null ) {
27
+ const functionName = functionMatch [ 1 ] || functionMatch [ 2 ] || 'constructor'
28
+ functionPositions . set ( functionName , { position : functionMatch . index } )
29
+ }
30
+
31
+ // Initialize functionComments with null for all functions
32
+ const functionComments = { }
33
+ for ( const [ functionName ] of functionPositions ) {
34
+ functionComments [ functionName ] = null
35
+ }
36
+
37
+ const multiLineCommentPattern = / \/ \* \* [ \s \S ] * ?\* \/ / g
38
+ let commentMatch
39
+
40
+ while ( ( commentMatch = multiLineCommentPattern . exec ( code ) ) !== null ) {
41
+ const commentStart = commentMatch . index
42
+ const commentEnd = commentStart + commentMatch [ 0 ] . length
43
+ let nextFunction = null
44
+ let minDistance = Infinity
45
+
46
+ for ( const [ funcName , funcInfo ] of functionPositions ) {
47
+ if ( funcInfo . position > commentEnd && funcInfo . position - commentEnd < minDistance ) {
48
+ minDistance = funcInfo . position - commentEnd
49
+ nextFunction = funcName
50
+ }
51
+ }
52
+
53
+ if ( nextFunction && minDistance < Infinity ) {
54
+ const betweenCommentAndFunction = code . slice ( commentEnd , functionPositions . get ( nextFunction ) . position )
55
+ const hasOtherFunction = betweenCommentAndFunction . match ( functionPattern )
56
+
57
+ if ( ! hasOtherFunction ) {
58
+ functionPositions . get ( nextFunction ) . commentStart = commentStart
59
+ }
60
+ }
61
+ }
62
+
63
+ for ( const [ functionName , funcInfo ] of functionPositions ) {
64
+ const functionStart = funcInfo . position
65
+ const functionStartLine = code . slice ( 0 , functionStart ) . split ( '\n' ) . length
66
+ const singleLineComments = [ ]
67
+
68
+ let currentLine = functionStartLine - 1
69
+ while ( currentLine > 0 ) {
70
+ const lineStart = code . split ( '\n' ) . slice ( 0 , currentLine - 1 ) . join ( '\n' ) . length + ( currentLine > 1 ? 1 : 0 )
71
+ const lineEnd = code . split ( '\n' ) . slice ( 0 , currentLine ) . join ( '\n' ) . length
72
+ const line = code . slice ( lineStart , lineEnd ) . trim ( )
73
+ if ( line . startsWith ( '//' ) ) {
74
+ singleLineComments . unshift ( line . slice ( 2 ) . trim ( ) )
75
+ currentLine --
76
+ } else if ( line === '' ) {
77
+ currentLine --
78
+ } else {
79
+ break
80
+ }
81
+ }
82
+ if ( singleLineComments . length > 0 ) {
83
+ funcInfo . singleLineComments = singleLineComments
84
+ }
85
+ }
86
+
87
+ for ( const [ functionName , funcInfo ] of functionPositions ) {
88
+ let processedComment = null
89
+
90
+ if ( funcInfo . commentStart !== undefined ) {
91
+ const comment = code . slice ( funcInfo . commentStart , funcInfo . position ) . trim ( )
92
+
93
+ if ( isSolidity ) {
94
+ const betweenCommentAndFunction = code . slice ( funcInfo . commentStart + comment . length , funcInfo . position )
95
+ const contractKeywordPos = betweenCommentAndFunction . indexOf ( 'contract' )
96
+
97
+ if ( contractKeywordPos === - 1 ) {
98
+ const contractCommentPattern = / \/ \* \* [ \s \S ] * ?\* \/ \s * c o n t r a c t \s + \w + / g
99
+ processedComment = comment
100
+ . replace ( contractCommentPattern , '' )
101
+ . trim ( )
102
+ if ( ! processedComment . startsWith ( '/**' ) ) {
103
+ processedComment = '/**' + processedComment
104
+ }
105
+ if ( ! processedComment . endsWith ( '*/' ) ) {
106
+ processedComment = processedComment + '*/'
107
+ }
108
+
109
+ processedComment = processedComment . split ( '\n' )
110
+ . filter ( line => {
111
+ const trimmed = line . trim ( )
112
+ return trimmed . startsWith ( '*' ) || trimmed === '/*' || trimmed === '*/' || trimmed === '/**'
113
+ } )
114
+ . join ( '\n' )
115
+ . trim ( )
116
+
117
+ if ( indentSize > 0 ) {
118
+ processedComment = applyIndentation ( processedComment , indentSize )
119
+ }
120
+
121
+ if ( ! processedComment . includes ( '@dev' ) && ! processedComment . includes ( '@param' ) && ! processedComment . includes ( '@return' ) ) {
122
+ processedComment = null
123
+ }
124
+ }
125
+ } else {
126
+ processedComment = comment . trim ( )
127
+ if ( ! processedComment . startsWith ( '/**' ) ) {
128
+ processedComment = '/**' + processedComment
129
+ }
130
+ if ( ! processedComment . endsWith ( '*/' ) ) {
131
+ processedComment = processedComment + '*/'
132
+ }
133
+ processedComment = processedComment . split ( '\n' )
134
+ . filter ( line => {
135
+ const trimmed = line . trim ( )
136
+ return trimmed . startsWith ( '*' ) || trimmed === '/*' || trimmed === '*/' || trimmed === '/**'
137
+ } )
138
+ . join ( '\n' )
139
+ . trim ( )
140
+
141
+ if ( indentSize > 0 ) {
142
+ processedComment = applyIndentation ( processedComment , indentSize )
143
+ }
144
+ }
145
+ }
146
+
147
+ if ( ! processedComment && funcInfo . singleLineComments && funcInfo . singleLineComments . length > 0 ) {
148
+ if ( isSolidity ) {
149
+ processedComment = '/**\n * @dev ' + funcInfo . singleLineComments . join ( '\n * ' ) + '\n */'
150
+ } else {
151
+ processedComment = '/**\n * ' + funcInfo . singleLineComments . join ( '\n * ' ) + '\n */'
152
+ }
153
+ if ( indentSize > 0 ) {
154
+ processedComment = applyIndentation ( processedComment , indentSize )
155
+ }
156
+ }
157
+
158
+ functionComments [ functionName ] = processedComment
159
+ }
160
+
161
+ return functionComments
162
+ }
163
+
164
+ // Helper function to apply indentation to comments
165
+ const applyIndentation = ( comment : string , indentSize : number ) : string => {
166
+ const indent = '\t' . repeat ( indentSize )
167
+ return comment . split ( '\n' ) . map ( line => {
168
+ if ( line . trim ( ) === '' ) return line
169
+ if ( line . trim ( ) === '*/' || line . trim ( ) === '/*' ) {
170
+ return indent + line . trim ( )
171
+ }
172
+ if ( line . trim ( ) . startsWith ( '*' ) ) {
173
+ const content = line . trim ( ) . slice ( 1 ) . trim ( )
174
+ return indent + '* ' + content
175
+ }
176
+ return indent + line
177
+ } ) . join ( '\n' )
178
+ }
0 commit comments