3
3
*--------------------------------------------------------*/
4
4
5
5
import { IFeature } from "../feature" ;
6
- import { TextDocumentChangeEvent , workspace , Disposable , Position , window , Range , EndOfLine , SnippetString } from "vscode" ;
6
+ import { TextDocumentChangeEvent , workspace , Disposable , Position , window , Range , EndOfLine , SnippetString , TextDocument } from "vscode" ;
7
7
import { LanguageClient , RequestType } from "vscode-languageclient" ;
8
8
9
9
export namespace CommentHelpRequest {
@@ -23,98 +23,155 @@ interface CommentHelpRequestResult {
23
23
enum SearchState { Searching , Locked , Found } ;
24
24
25
25
export class HelpCompletionFeature implements IFeature {
26
- private readonly triggerCharactersBlockComment : string ;
27
- private readonly triggerCharactersLineComment : string ;
28
- private triggerCharactersFound : string ;
26
+ private helpCompletionProvider : HelpCompletionProvider ;
29
27
private languageClient : LanguageClient ;
30
28
private disposable : Disposable ;
31
- private searchState : SearchState ;
32
- private get isBlockComment ( ) : boolean {
33
- return this . triggerCharactersFound !== undefined &&
34
- this . triggerCharactersFound === this . triggerCharactersBlockComment ;
35
- }
36
29
37
30
constructor ( ) {
38
- this . triggerCharactersBlockComment = "<#" ;
39
- this . triggerCharactersLineComment = "##" ;
31
+ this . helpCompletionProvider = new HelpCompletionProvider ( ) ;
40
32
let subscriptions = [ ] ;
41
33
workspace . onDidChangeTextDocument ( this . onEvent , this , subscriptions ) ;
42
- this . searchState = SearchState . Searching ;
43
34
this . disposable = Disposable . from ( ...subscriptions ) ;
44
35
}
45
36
46
37
setLanguageClient ( languageClient : LanguageClient ) {
47
38
this . languageClient = languageClient ;
39
+ this . helpCompletionProvider . languageClient = languageClient ;
48
40
}
49
41
50
42
dispose ( ) {
51
43
this . disposable . dispose ( ) ;
52
44
}
53
45
54
46
onEvent ( changeEvent : TextDocumentChangeEvent ) : void {
55
- // todo split this method into logical components
56
- // todo create a helpcompletion provider class
57
- // todo associate state with a given document
58
- let text = changeEvent . contentChanges [ 0 ] . text ;
59
- switch ( this . searchState ) {
47
+ this . helpCompletionProvider . updateState (
48
+ changeEvent . document ,
49
+ changeEvent . contentChanges [ 0 ] . text ,
50
+ changeEvent . contentChanges [ 0 ] . range ) ;
51
+
52
+ // todo raise an event when trigger is found, and attach complete() to the event.
53
+ if ( this . helpCompletionProvider . triggerFound ) {
54
+ this . helpCompletionProvider . reset ( ) ;
55
+ this . helpCompletionProvider . complete ( ) ;
56
+ }
57
+
58
+ }
59
+ }
60
+
61
+ class TriggerFinder {
62
+ private state : SearchState ;
63
+ private document : TextDocument ;
64
+ private count : number ;
65
+ constructor ( private triggerCharacters : string ) {
66
+ this . state = SearchState . Searching ;
67
+ this . count = 0 ;
68
+ }
69
+
70
+ public get found ( ) : boolean {
71
+ return this . state === SearchState . Found ;
72
+ }
73
+
74
+ public updateState ( document : TextDocument , changeText : string ) : void {
75
+ switch ( this . state ) {
60
76
case SearchState . Searching :
61
- if ( text . length === 1 ) {
62
- if ( text [ 0 ] === this . triggerCharactersBlockComment [ 0 ] ) {
63
- this . searchState = SearchState . Locked ;
64
- this . triggerCharactersFound = this . triggerCharactersBlockComment ;
65
- }
66
- else if ( text [ 0 ] === this . triggerCharactersLineComment [ 0 ] ) {
67
- this . searchState = SearchState . Locked ;
68
- this . triggerCharactersFound = this . triggerCharactersLineComment ;
69
- }
77
+ if ( changeText . length === 1 && changeText [ 0 ] === this . triggerCharacters [ this . count ] ) {
78
+ this . state = SearchState . Locked ;
79
+ this . document = document ;
80
+ this . count ++ ;
70
81
}
71
82
break ;
72
83
73
84
case SearchState . Locked :
74
- if ( text . length === 1 && text [ 0 ] === this . triggerCharactersFound [ 1 ] ) {
75
- this . searchState = SearchState . Found ;
85
+ if ( changeText . length === 1 && changeText [ 0 ] === this . triggerCharacters [ this . count ] && document === this . document ) {
86
+ this . count ++ ;
87
+ if ( this . count === this . triggerCharacters . length ) {
88
+ this . state = SearchState . Found ;
89
+ }
76
90
}
77
91
else {
78
- this . searchState = SearchState . Searching ;
92
+ this . reset ( ) ;
79
93
}
80
94
break ;
95
+
96
+ default :
97
+ this . reset ( ) ;
98
+ break ;
81
99
}
100
+ }
101
+
102
+ public reset ( ) : void {
103
+ this . state = SearchState . Searching ;
104
+ this . count = 0 ;
105
+ }
106
+ }
82
107
83
- let r = changeEvent . contentChanges [ 0 ] . range ;
84
- console . log ( `Search State: ${ this . searchState . toString ( ) } ; Range: (${ r . start . line } , ${ r . start . character } ), (${ r . end . line } , ${ r . end . character } )` ) ;
85
- if ( this . searchState === SearchState . Found ) {
86
- this . searchState = SearchState . Searching ;
87
- if ( this . languageClient ) {
88
- let change = changeEvent . contentChanges [ 0 ] ;
89
- let triggerStartPos = change . range . start ;
90
- let triggerEndPos = change . range . end ;
91
- let doc = changeEvent . document ;
92
- this . languageClient . sendRequest (
93
- CommentHelpRequest . type ,
94
- {
95
- documentUri : changeEvent . document . uri . toString ( ) ,
96
- triggerPosition : triggerStartPos ,
97
- blockComment : this . isBlockComment
98
- } ) . then ( result => {
99
- if ( result === undefined ) {
100
- return ;
101
- }
102
-
103
- let content = result . content ;
104
- if ( content === undefined ) {
105
- return ;
106
- }
107
-
108
- // todo add indentation level to the help content
109
- let editor = window . activeTextEditor ;
110
- let replaceRange = new Range ( triggerStartPos . translate ( 0 , - 1 ) , triggerStartPos . translate ( 0 , 1 ) ) ;
111
-
112
- // Trim the last empty line and join the strings.
113
- let text = content . slice ( 0 , - 1 ) . join ( this . getEOL ( doc . eol ) ) ;
114
- editor . insertSnippet ( new SnippetString ( text ) , replaceRange ) ;
115
- } ) ;
116
- }
108
+ class HelpCompletionProvider {
109
+ private triggerFinderBlockComment : TriggerFinder ;
110
+ private triggerFinderLineComment : TriggerFinder ;
111
+ private lastChangeText : string ;
112
+ private lastChangeRange : Range ;
113
+ private lastDocument : TextDocument ;
114
+ private langClient : LanguageClient ;
115
+
116
+ constructor ( ) {
117
+ this . triggerFinderBlockComment = new TriggerFinder ( "<#" ) ;
118
+ this . triggerFinderLineComment = new TriggerFinder ( "##" ) ;
119
+ }
120
+
121
+ public get triggerFound ( ) : boolean {
122
+ return this . triggerFinderBlockComment . found || this . triggerFinderLineComment . found ;
123
+ }
124
+
125
+ public set languageClient ( value : LanguageClient ) {
126
+ this . langClient = value ;
127
+ }
128
+
129
+ public updateState ( document : TextDocument , changeText : string , changeRange : Range ) : void {
130
+ this . lastDocument = document ;
131
+ this . lastChangeText = changeText ;
132
+ this . lastChangeRange = changeRange ;
133
+ this . triggerFinderBlockComment . updateState ( document , changeText ) ;
134
+ this . triggerFinderLineComment . updateState ( document , changeText ) ;
135
+ }
136
+
137
+ public reset ( ) : void {
138
+ this . triggerFinderBlockComment . reset ( ) ;
139
+ this . triggerFinderLineComment . reset ( ) ;
140
+ }
141
+
142
+ public complete ( ) : Thenable < void > {
143
+ if ( this . langClient === undefined ) {
144
+ return ;
117
145
}
146
+
147
+ let change = this . lastChangeText ;
148
+ let triggerStartPos = this . lastChangeRange . start ;
149
+ let triggerEndPos = this . lastChangeRange . end ;
150
+ let doc = this . lastDocument ;
151
+ this . langClient . sendRequest (
152
+ CommentHelpRequest . type ,
153
+ {
154
+ documentUri : doc . uri . toString ( ) ,
155
+ triggerPosition : triggerStartPos ,
156
+ blockComment : this . triggerFinderBlockComment . found
157
+ } ) . then ( result => {
158
+ if ( result === undefined ) {
159
+ return ;
160
+ }
161
+
162
+ let content = result . content ;
163
+ if ( content === undefined ) {
164
+ return ;
165
+ }
166
+
167
+ // todo add indentation level to the help content
168
+ let editor = window . activeTextEditor ;
169
+ let replaceRange = new Range ( triggerStartPos . translate ( 0 , - 1 ) , triggerStartPos . translate ( 0 , 1 ) ) ;
170
+
171
+ // Trim the last empty line and join the strings.
172
+ let text = content . slice ( 0 , - 1 ) . join ( this . getEOL ( doc . eol ) ) ;
173
+ editor . insertSnippet ( new SnippetString ( text ) , replaceRange ) ;
174
+ } ) ;
118
175
}
119
176
120
177
private getEOL ( eol : EndOfLine ) : string {
0 commit comments