33 *--------------------------------------------------------*/
44
55import { 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" ;
77import { LanguageClient , RequestType } from "vscode-languageclient" ;
88
99export namespace CommentHelpRequest {
@@ -23,98 +23,155 @@ interface CommentHelpRequestResult {
2323enum SearchState { Searching , Locked , Found } ;
2424
2525export class HelpCompletionFeature implements IFeature {
26- private readonly triggerCharactersBlockComment : string ;
27- private readonly triggerCharactersLineComment : string ;
28- private triggerCharactersFound : string ;
26+ private helpCompletionProvider : HelpCompletionProvider ;
2927 private languageClient : LanguageClient ;
3028 private disposable : Disposable ;
31- private searchState : SearchState ;
32- private get isBlockComment ( ) : boolean {
33- return this . triggerCharactersFound !== undefined &&
34- this . triggerCharactersFound === this . triggerCharactersBlockComment ;
35- }
3629
3730 constructor ( ) {
38- this . triggerCharactersBlockComment = "<#" ;
39- this . triggerCharactersLineComment = "##" ;
31+ this . helpCompletionProvider = new HelpCompletionProvider ( ) ;
4032 let subscriptions = [ ] ;
4133 workspace . onDidChangeTextDocument ( this . onEvent , this , subscriptions ) ;
42- this . searchState = SearchState . Searching ;
4334 this . disposable = Disposable . from ( ...subscriptions ) ;
4435 }
4536
4637 setLanguageClient ( languageClient : LanguageClient ) {
4738 this . languageClient = languageClient ;
39+ this . helpCompletionProvider . languageClient = languageClient ;
4840 }
4941
5042 dispose ( ) {
5143 this . disposable . dispose ( ) ;
5244 }
5345
5446 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 ) {
6076 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 ++ ;
7081 }
7182 break ;
7283
7384 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+ }
7690 }
7791 else {
78- this . searchState = SearchState . Searching ;
92+ this . reset ( ) ;
7993 }
8094 break ;
95+
96+ default :
97+ this . reset ( ) ;
98+ break ;
8199 }
100+ }
101+
102+ public reset ( ) : void {
103+ this . state = SearchState . Searching ;
104+ this . count = 0 ;
105+ }
106+ }
82107
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 ;
117145 }
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+ } ) ;
118175 }
119176
120177 private getEOL ( eol : EndOfLine ) : string {
0 commit comments