11'use strict' ;
22
3- import * as fs from 'fs' ;
4- import * as os from 'os' ;
5- import * as path from 'path' ;
63import * as vscode from 'vscode' ;
7- import {
8- nREPLClient
9- } from './nreplClient' ;
10- import {
11- ClojureProvider
12- } from './clojureProvider' ;
4+
5+ import { ClojureProvider } from './clojureProvider' ;
6+ import * as cljParser from './cljParser' ;
7+
8+ const PARAMETER_OPEN = `[` ;
9+ const PARAMETER_CLOSE = `]` ;
10+ const PARAMETER_REST = `&` ;
11+ const UNSUPPORTED_SIGNATURE_NAMES = [ '.' , 'new' , 'fn' , 'set!' ] ; // they have forms that do not follow the same rules as other special forms
12+ const SPECIAL_FORM_PARAMETER_REST = `*` ;
1313
1414export class ClojureSignatureProvider extends ClojureProvider implements vscode . SignatureHelpProvider {
1515
16- provideSignatureHelp ( document : vscode . TextDocument , position : vscode . Position , token : vscode . CancellationToken ) : Thenable < vscode . SignatureHelp > {
17- return new Promise < vscode . SignatureHelp > ( ( resolve , reject ) => {
18- let wordRange = document . getWordRangeAtPosition ( position ) ;
19- if ( wordRange === undefined ) {
20- return reject ( ) ; //resolve(new vscode.Hover('Docstring not found'));
21- }
22- let currentWord : string ;
23- currentWord = document . lineAt ( position . line ) . text . slice ( wordRange . start . character , wordRange . end . character ) ;
24- let ns = this . getNamespace ( document . getText ( ) ) ;
25-
26- let nrepl = this . getNREPL ( ) ;
27- nrepl . info ( currentWord , ns , ( info ) => {
28- if ( info . doc ) {
29- let signatureHelp = new vscode . SignatureHelp ( ) ;
30- signatureHelp . activeParameter = 0 ;
31- signatureHelp . activeSignature = 0 ;
32- signatureHelp . signatures = [ new vscode . SignatureInformation ( 'a b c' ) ]
33- resolve ( signatureHelp ) ;
34- }
35- reject ( ) ;
16+ provideSignatureHelp ( document : vscode . TextDocument , position : vscode . Position , token : vscode . CancellationToken ) : Thenable < vscode . SignatureHelp > {
17+ const textToGetInfo = document . getText ( new vscode . Range ( new vscode . Position ( 0 , 0 ) , position ) ) ;
18+ const exprInfo = cljParser . getExpressionInfo ( textToGetInfo ) ;
19+ if ( ! exprInfo )
20+ return ;
21+
22+ const ns = this . getNamespace ( document . getText ( ) ) ;
23+
24+ return new Promise < vscode . SignatureHelp > ( ( resolve , reject ) => {
25+ this . getNREPL ( ) . info ( exprInfo . functionName , ns , info => {
26+ if ( ! info . name ) // sometimes info brings just a list of suggestions (example: .MAX_VALUE)
27+ return resolve ( ) ;
28+
29+ if ( ! ! info [ 'special-form' ] )
30+ return resolve ( getSpecialFormSignatureHelp ( info , exprInfo . parameterPosition ) ) ;
31+
32+ return resolve ( getFunctionSignatureHelp ( info , exprInfo . parameterPosition ) ) ;
3633 } ) ;
3734 } ) ;
3835 }
39- }
36+
37+ }
38+
39+ function getSpecialFormSignatureHelp ( info : any , parameterPosition : number ) : vscode . SignatureHelp {
40+ if ( UNSUPPORTED_SIGNATURE_NAMES . indexOf ( info . name ) > - 1 ) {
41+ const signatureHelp = new vscode . SignatureHelp ( ) ;
42+ signatureHelp . signatures = [ new vscode . SignatureInformation ( `${ info . name } *special form* ${ info [ 'forms-str' ] } ` , info . doc ) ] ;
43+ signatureHelp . activeSignature = 0 ;
44+
45+ return signatureHelp ;
46+ }
47+
48+ const forms : string = info [ 'forms-str' ] ;
49+ const [ functionName , ...parameters ] = forms . substring ( 3 , forms . length - 1 ) . split ( ' ' ) ;
50+ const parameterInfos = parameters . map ( parameter => new vscode . ParameterInformation ( parameter ) ) ;
51+
52+ const sigInfo = new vscode . SignatureInformation ( `${ info . name } *special form* [${ parameterInfos . map ( pi => pi . label ) . join ( `\n` ) } ]` , info . doc ) ;
53+ sigInfo . parameters = parameterInfos ;
54+
55+ if ( parameterPosition + 1 > sigInfo . parameters . length && sigInfo . parameters [ sigInfo . parameters . length - 1 ] . label . endsWith ( SPECIAL_FORM_PARAMETER_REST ) )
56+ parameterPosition = sigInfo . parameters . length - 1 ;
57+
58+ const signatureHelp = new vscode . SignatureHelp ( ) ;
59+ signatureHelp . signatures = [ sigInfo ] ;
60+ signatureHelp . activeParameter = parameterPosition ;
61+ signatureHelp . activeSignature = 0 ;
62+
63+ return signatureHelp ;
64+ }
65+
66+ function getFunctionSignatureHelp ( info : any , parameterPosition : number ) : vscode . SignatureHelp {
67+ const signatures = getFunctionSignatureInfos ( info ) ;
68+ signatures . sort ( ( sig1 , sig2 ) => sig1 . parameters . length - sig2 . parameters . length ) ;
69+
70+ let activeSignature = signatures . findIndex ( signature => signature . parameters . length >= parameterPosition + 1 ) ;
71+ if ( activeSignature === - 1 ) {
72+ activeSignature = signatures . findIndex ( signature => signature . parameters . some ( param => param . label . startsWith ( PARAMETER_REST ) ) ) ;
73+ if ( activeSignature !== - 1 )
74+ parameterPosition = signatures [ activeSignature ] . parameters . length - 1 ;
75+ }
76+ if ( activeSignature === - 1 )
77+ activeSignature = 0 ;
78+
79+ const signatureHelp = new vscode . SignatureHelp ( ) ;
80+ signatureHelp . signatures = signatures ;
81+ signatureHelp . activeParameter = parameterPosition ;
82+ signatureHelp . activeSignature = activeSignature ;
83+
84+ return signatureHelp ;
85+ }
86+
87+ function getFunctionSignatureInfos ( info : any ) : vscode . SignatureInformation [ ] {
88+ const arglists : string = info [ 'arglists-str' ] ;
89+
90+ const sigParamStarts : number [ ] = [ ] ;
91+ const sigParamStops : number [ ] = [ ] ;
92+ let nestingLevel = 0 ;
93+ for ( let i = 0 ; i < arglists . length ; i ++ ) {
94+ if ( arglists [ i ] === PARAMETER_OPEN ) {
95+ if ( nestingLevel === 0 )
96+ sigParamStarts . push ( i ) ;
97+ nestingLevel ++ ;
98+ }
99+ if ( arglists [ i ] === PARAMETER_CLOSE ) {
100+ nestingLevel -- ;
101+ if ( nestingLevel === 0 )
102+ sigParamStops . push ( i ) ;
103+ }
104+ }
105+
106+ return sigParamStarts
107+ . map ( ( sigParamStart , index ) => arglists . substring ( sigParamStart , sigParamStops [ index ] + 1 ) )
108+ . map ( signatureParameter => {
109+ const parameterInfos = getFunctionParameterInfos ( signatureParameter ) ;
110+ const sigInfo = new vscode . SignatureInformation ( `${ info . ns } /${ info . name } [${ parameterInfos . map ( pi => pi . label ) . join ( `\n` ) } ]` ) ;
111+ sigInfo . documentation = info . doc ;
112+ sigInfo . parameters = parameterInfos ;
113+ return sigInfo ;
114+ } ) ;
115+ }
116+
117+ function getFunctionParameterInfos ( signatureParameter : string ) : vscode . ParameterInformation [ ] {
118+ signatureParameter = signatureParameter . substring ( 1 , signatureParameter . length - 1 ) ; // removing external brackets
119+ const paramStarts : number [ ] = [ ] ;
120+ const paramStops : number [ ] = [ ] ;
121+ let insideParameter = false ;
122+ let bracketsNestingLevel = 0 ;
123+ for ( let i = 0 ; i < signatureParameter . length ; i ++ ) {
124+ const char = signatureParameter [ i ] ;
125+
126+ if ( ! insideParameter ) {
127+ insideParameter = true ;
128+ paramStarts . push ( i ) ;
129+ if ( char === PARAMETER_OPEN )
130+ bracketsNestingLevel ++ ;
131+ if ( char === PARAMETER_REST )
132+ break ;
133+ } else {
134+ if ( char === PARAMETER_OPEN )
135+ bracketsNestingLevel ++ ;
136+ if ( char === PARAMETER_CLOSE )
137+ bracketsNestingLevel -- ;
138+ if ( char === PARAMETER_CLOSE && bracketsNestingLevel === 0 ) {
139+ paramStops . push ( i ) ;
140+ insideParameter = false ;
141+ }
142+ if ( cljParser . R_CLJ_WHITE_SPACE . test ( char ) && bracketsNestingLevel === 0 ) {
143+ paramStops . push ( i ) ;
144+ insideParameter = false ;
145+ }
146+ }
147+ }
148+ paramStops . push ( signatureParameter . length ) ;
149+
150+ return paramStarts
151+ . map ( ( paramStart , index ) => signatureParameter . substring ( paramStart , paramStops [ index ] + 1 ) )
152+ . map ( parameter => new vscode . ParameterInformation ( parameter ) ) ;
153+ }
0 commit comments