1+ /*---------------------------------------------------------------------------------------------
2+ * Copyright (c) Microsoft Corporation. All rights reserved.
3+ * Licensed under the MIT License. See License.txt in the project root for license information.
4+ *--------------------------------------------------------------------------------------------*/
5+
6+ import * as vscode from 'vscode' ;
7+ import * as serverUtils from '../omnisharp/utils' ;
8+ import * as protocol from '../omnisharp/protocol' ;
9+ import { OmniSharpServer } from '../omnisharp/server' ;
10+ import { FixAllScope , FixAllItem , FileModificationType } from '../omnisharp/protocol' ;
11+ import { Uri } from 'vscode' ;
12+ import CompositeDisposable from '../CompositeDisposable' ;
13+ import AbstractProvider from './abstractProvider' ;
14+ import { toRange2 } from '../omnisharp/typeConversion' ;
15+ import { LanguageMiddlewareFeature } from '../omnisharp/LanguageMiddlewareFeature' ;
16+
17+ export class FixAllProvider extends AbstractProvider implements vscode . CodeActionProvider {
18+ public constructor ( private server : OmniSharpServer , languageMiddlewareFeature : LanguageMiddlewareFeature ) {
19+ super ( server , languageMiddlewareFeature ) ;
20+ let disposable = new CompositeDisposable ( ) ;
21+ disposable . add ( vscode . commands . registerCommand ( 'o.fixAll.solution' , async ( ) => this . fixAllMenu ( server , protocol . FixAllScope . Solution ) ) ) ;
22+ disposable . add ( vscode . commands . registerCommand ( 'o.fixAll.project' , async ( ) => this . fixAllMenu ( server , protocol . FixAllScope . Project ) ) ) ;
23+ disposable . add ( vscode . commands . registerCommand ( 'o.fixAll.document' , async ( ) => this . fixAllMenu ( server , protocol . FixAllScope . Document ) ) ) ;
24+ this . addDisposables ( disposable ) ;
25+ }
26+
27+ public async provideCodeActions (
28+ document : vscode . TextDocument ,
29+ _range : vscode . Range | vscode . Selection ,
30+ context : vscode . CodeActionContext ,
31+ _token : vscode . CancellationToken ,
32+ ) : Promise < vscode . CodeAction [ ] > {
33+ console . log ( context ) ;
34+ if ( ! context . only ) {
35+ return [ ] ;
36+ }
37+
38+ if ( context . only . value === "source.fixAll.csharp" ) {
39+ await this . applyFixes ( document . fileName , FixAllScope . Document , undefined ) ;
40+ }
41+
42+ return [ ] ;
43+ }
44+
45+ private async fixAllMenu ( server : OmniSharpServer , scope : protocol . FixAllScope ) : Promise < void > {
46+ let availableFixes = await serverUtils . getFixAll ( server , { FileName : vscode . window . activeTextEditor . document . fileName , Scope : scope } ) ;
47+
48+ let targets = availableFixes . Items . map ( x => `${ x . Id } : ${ x . Message } ` ) ;
49+
50+ if ( scope === protocol . FixAllScope . Document ) {
51+ targets = [ "Fix all issues" , ...targets ] ;
52+ }
53+
54+ return vscode . window . showQuickPick ( targets , {
55+ matchOnDescription : true ,
56+ placeHolder : `Select fix all action`
57+ } ) . then ( async selectedAction => {
58+ let filter : FixAllItem [ ] = undefined ;
59+
60+ if ( selectedAction === undefined ) {
61+ return ;
62+ }
63+
64+ if ( selectedAction !== "Fix all issues" ) {
65+ let actionTokens = selectedAction . split ( ":" ) ;
66+ filter = [ { Id : actionTokens [ 0 ] , Message : actionTokens [ 1 ] } ] ;
67+ }
68+
69+ await this . applyFixes ( vscode . window . activeTextEditor . document . fileName , scope , filter ) ;
70+ } ) ;
71+ }
72+
73+ private async applyFixes ( fileName : string , scope : FixAllScope , fixAllFilter : FixAllItem [ ] ) : Promise < boolean | string | { } > {
74+ let response = await serverUtils . runFixAll ( this . server , { FileName : fileName , Scope : scope , FixAllFilter : fixAllFilter , WantsAllCodeActionOperations : true , WantsTextChanges : true } ) ;
75+
76+ if ( response && Array . isArray ( response . Changes ) ) {
77+ let edit = new vscode . WorkspaceEdit ( ) ;
78+
79+ let fileToOpen : Uri = null ;
80+ let renamedFiles : Uri [ ] = [ ] ;
81+
82+ for ( let change of response . Changes ) {
83+ if ( change . ModificationType == FileModificationType . Renamed )
84+ {
85+ // The file was renamed. Omnisharp has already persisted
86+ // the right changes to disk. We don't need to try to
87+ // apply text changes (and will skip this file if we see an edit)
88+ renamedFiles . push ( Uri . file ( change . FileName ) ) ;
89+ }
90+ }
91+
92+ for ( let change of response . Changes ) {
93+ if ( change . ModificationType == FileModificationType . Opened )
94+ {
95+ // The CodeAction requested that we open a file.
96+ // Record that file name and keep processing CodeActions.
97+ // If a CodeAction requests that we open multiple files
98+ // we only open the last one (what would it mean to open multiple files?)
99+ fileToOpen = vscode . Uri . file ( change . FileName ) ;
100+ }
101+
102+ if ( change . ModificationType == FileModificationType . Modified )
103+ {
104+ let uri = vscode . Uri . file ( change . FileName ) ;
105+ if ( renamedFiles . some ( r => r == uri ) )
106+ {
107+ // This file got renamed. Omnisharp has already
108+ // persisted the new file with any applicable changes.
109+ continue ;
110+ }
111+
112+ let edits : vscode . TextEdit [ ] = [ ] ;
113+ for ( let textChange of change . Changes ) {
114+ edits . push ( vscode . TextEdit . replace ( toRange2 ( textChange ) , textChange . NewText ) ) ;
115+ }
116+
117+ edit . set ( uri , edits ) ;
118+ }
119+ }
120+
121+ let applyEditPromise = vscode . workspace . applyEdit ( edit ) ;
122+
123+ // Unfortunately, the textEditor.Close() API has been deprecated
124+ // and replaced with a command that can only close the active editor.
125+ // If files were renamed that weren't the active editor, their tabs will
126+ // be left open and marked as "deleted" by VS Code
127+ let next = applyEditPromise ;
128+ if ( renamedFiles . some ( r => r . fsPath == vscode . window . activeTextEditor . document . uri . fsPath ) )
129+ {
130+ next = applyEditPromise . then ( _ =>
131+ {
132+ return vscode . commands . executeCommand ( "workbench.action.closeActiveEditor" ) ;
133+ } ) ;
134+ }
135+
136+ return fileToOpen != null
137+ ? next . then ( _ =>
138+ {
139+ return vscode . commands . executeCommand ( "vscode.open" , fileToOpen ) ;
140+ } )
141+ : next ;
142+ }
143+ }
144+ }
0 commit comments