11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4+ 'use strict' ;
5+
46import { inject , injectable } from 'inversify' ;
5- import { CancellationToken , OutputChannel , TextDocument , Uri } from 'vscode' ;
6- import { IConfigurationService , ILogger , Product } from '../common/types' ;
7+ import {
8+ CancellationToken , OutputChannel , TextDocument , Uri
9+ } from 'vscode' ;
10+ import {
11+ IConfigurationService , ILogger , Product
12+ } from '../common/types' ;
713import { IServiceContainer } from '../ioc/types' ;
814import { Bandit } from './bandit' ;
915import { Flake8 } from './flake8' ;
@@ -14,7 +20,9 @@ import { Prospector } from './prospector';
1420import { PyDocStyle } from './pydocstyle' ;
1521import { PyLama } from './pylama' ;
1622import { Pylint } from './pylint' ;
17- import { ILinter , ILinterInfo , ILinterManager , ILintMessage } from './types' ;
23+ import {
24+ IAvailableLinterActivator , ILinter , ILinterInfo , ILinterManager , ILintMessage
25+ } from './types' ;
1826
1927class DisabledLinter implements ILinter {
2028 constructor ( private configService : IConfigurationService ) { }
@@ -31,8 +39,9 @@ export class LinterManager implements ILinterManager {
3139 private lintingEnabledSettingName = 'enabled' ;
3240 private linters : ILinterInfo [ ] ;
3341 private configService : IConfigurationService ;
42+ private checkedForInstalledLinters : boolean = false ;
3443
35- constructor ( @inject ( IServiceContainer ) serviceContainer : IServiceContainer ) {
44+ constructor ( @inject ( IServiceContainer ) private serviceContainer : IServiceContainer ) {
3645 this . configService = serviceContainer . get < IConfigurationService > ( IConfigurationService ) ;
3746 this . linters = [
3847 new LinterInfo ( Product . bandit , 'bandit' , this . configService ) ,
@@ -58,40 +67,49 @@ export class LinterManager implements ILinterManager {
5867 throw new Error ( 'Invalid linter' ) ;
5968 }
6069
61- public isLintingEnabled ( resource ?: Uri ) : boolean {
70+ public async isLintingEnabled ( silent : boolean , resource ?: Uri ) : Promise < boolean > {
6271 const settings = this . configService . getSettings ( resource ) ;
63- return ( settings . linting [ this . lintingEnabledSettingName ] as boolean ) && this . getActiveLinters ( resource ) . length > 0 ;
72+ const activeLintersPresent = await this . getActiveLinters ( silent , resource ) ;
73+ return ( settings . linting [ this . lintingEnabledSettingName ] as boolean ) && activeLintersPresent . length > 0 ;
6474 }
6575
6676 public async enableLintingAsync ( enable : boolean , resource ?: Uri ) : Promise < void > {
6777 await this . configService . updateSetting ( `linting.${ this . lintingEnabledSettingName } ` , enable , resource ) ;
68-
69- // If nothing is enabled, fix it up to PyLint (default).
70- if ( enable && this . getActiveLinters ( resource ) . length === 0 ) {
71- await this . setActiveLintersAsync ( [ Product . pylint ] , resource ) ;
72- }
7378 }
7479
75- public getActiveLinters ( resource ?: Uri ) : ILinterInfo [ ] {
80+ public async getActiveLinters ( silent : boolean , resource ?: Uri ) : Promise < ILinterInfo [ ] > {
81+ if ( ! silent ) {
82+ await this . enableUnconfiguredLinters ( resource ) ;
83+ }
7684 return this . linters . filter ( x => x . isEnabled ( resource ) ) ;
7785 }
7886
7987 public async setActiveLintersAsync ( products : Product [ ] , resource ?: Uri ) : Promise < void > {
80- const active = this . getActiveLinters ( resource ) ;
81- for ( const x of active ) {
82- await x . enableAsync ( false , resource ) ;
83- }
84- if ( products . length > 0 ) {
85- const toActivate = this . linters . filter ( x => products . findIndex ( p => x . product === p ) >= 0 ) ;
86- for ( const x of toActivate ) {
87- await x . enableAsync ( true , resource ) ;
88+ // ensure we only allow valid linters to be set, otherwise leave things alone.
89+ // filter out any invalid products:
90+ const validProducts = products . filter ( product => {
91+ const foundIndex = this . linters . findIndex ( validLinter => validLinter . product === product ) ;
92+ return foundIndex !== - 1 ;
93+ } ) ;
94+
95+ // if we have valid linter product(s), enable only those
96+ if ( validProducts . length > 0 ) {
97+ const active = await this . getActiveLinters ( true , resource ) ;
98+ for ( const x of active ) {
99+ await x . enableAsync ( false , resource ) ;
100+ }
101+ if ( products . length > 0 ) {
102+ const toActivate = this . linters . filter ( x => products . findIndex ( p => x . product === p ) >= 0 ) ;
103+ for ( const x of toActivate ) {
104+ await x . enableAsync ( true , resource ) ;
105+ }
106+ await this . enableLintingAsync ( true , resource ) ;
88107 }
89- await this . enableLintingAsync ( true , resource ) ;
90108 }
91109 }
92110
93- public createLinter ( product : Product , outputChannel : OutputChannel , serviceContainer : IServiceContainer , resource ?: Uri ) : ILinter {
94- if ( ! this . isLintingEnabled ( resource ) ) {
111+ public async createLinter ( product : Product , outputChannel : OutputChannel , serviceContainer : IServiceContainer , resource ?: Uri ) : Promise < ILinter > {
112+ if ( ! await this . isLintingEnabled ( true , resource ) ) {
95113 return new DisabledLinter ( this . configService ) ;
96114 }
97115 const error = 'Linter manager: Unknown linter' ;
@@ -118,4 +136,24 @@ export class LinterManager implements ILinterManager {
118136 }
119137 throw new Error ( error ) ;
120138 }
139+
140+ protected async enableUnconfiguredLinters ( resource ?: Uri ) : Promise < boolean > {
141+ // if we've already checked during this session, don't bother again
142+ if ( this . checkedForInstalledLinters ) {
143+ return false ;
144+ }
145+ this . checkedForInstalledLinters = true ;
146+
147+ // only check & ask the user if they'd like to enable pylint
148+ const pylintInfo = this . linters . find (
149+ ( linter : ILinterInfo ) => linter . id === 'pylint'
150+ ) ;
151+
152+ // If linting is disabled, don't bother checking further.
153+ if ( pylintInfo && await this . isLintingEnabled ( true , resource ) ) {
154+ const activator = this . serviceContainer . get < IAvailableLinterActivator > ( IAvailableLinterActivator ) ;
155+ return activator . promptIfLinterAvailable ( pylintInfo , resource ) ;
156+ }
157+ return false ;
158+ }
121159}
0 commit comments