11"use strict" ;
22import * as child_process from 'child_process' ;
33import * as path from "path" ;
4+ import * as fs from "fs" ;
45import * as vscode from "vscode" ;
56import * as settings from "./../common/configSettings" ;
67import * as utils from "./../common/utils" ;
@@ -9,13 +10,22 @@ let ncp = require("copy-paste");
910// where to find the Python binary within a conda env
1011const CONDA_RELATIVE_PY_PATH = utils . IS_WINDOWS ? [ 'python' ] : [ 'bin' , 'python' ]
1112const REPLACE_PYTHONPATH_REGEXP = / ( " p y t h o n \. p y t h o n P a t h " \s * : \s * ) " ( .* ) " / g;
13+ const CHECK_PYTHON_INTERPRETER_REGEXP = utils . IS_WINDOWS ? / ^ p y t h o n ( \d + ( .\d + ) ? ) ? \. e x e $ / : / ^ p y t h o n ( \d + ( .\d + ) ? ) ? $ / ;
1214
1315interface PythonPathSuggestion {
1416 label : string , // myenvname
1517 path : string , // /full/path/to/bin/python
1618 type : string // conda
1719}
1820
21+ interface PythonPathQuickPickItem extends vscode . QuickPickItem {
22+ path : string
23+ }
24+
25+ function isPythonInterpreter ( filePath : string ) : boolean {
26+ return CHECK_PYTHON_INTERPRETER_REGEXP . test ( filePath ) ;
27+ }
28+
1929function getSearchPaths ( ) : string [ ] {
2030 if ( utils . IS_WINDOWS ) {
2131 return [
@@ -35,16 +45,9 @@ function getSearchPaths(): string[] {
3545 'C:\\Program Files\\Python 2.7' ,
3646 'C:\\Program Files\\Python 3.4' ,
3747 'C:\\Program Files\\Python 3.5'
38- ] . map ( p => path . join ( p , 'python.exe' ) ) ;
48+ ] ;
3949 } else {
40- const pyPaths = [ ] ;
41- const pyExecutables = [ 'python' , 'python2.6' , 'python2.7' , 'python3.3' , 'python3.4' , 'python3.5' ] ;
42- [ '/usr/local/bin' , '/usr/bin' , '/bin' , '/usr/sbin' , '/sbin' ] . forEach ( p => {
43- pyExecutables . forEach ( pyExecName => {
44- pyPaths . push ( path . join ( p , pyExecName ) ) ;
45- } ) ;
46- } ) ;
47- return pyPaths ;
50+ return [ '/usr/local/bin' , '/usr/bin' , '/bin' , '/usr/sbin' , '/sbin' ] ;
4851 }
4952}
5053
@@ -72,14 +75,66 @@ export function activateSetInterpreterProvider() {
7275 vscode . commands . registerCommand ( "python.setInterpreter" , setInterpreter ) ;
7376}
7477
78+ function lookForInterpretersInPath ( pathToCheck : string ) : Promise < string [ ] > {
79+ return new Promise < string [ ] > ( resolve => {
80+ // Now look for Interpreters in this directory
81+ fs . readdir ( pathToCheck , ( err , subDirs ) => {
82+ if ( err ) {
83+ return resolve ( [ ] ) ;
84+ }
85+ const interpreters = subDirs
86+ . filter ( subDir => CHECK_PYTHON_INTERPRETER_REGEXP . test ( subDir ) )
87+ . map ( subDir => path . join ( pathToCheck , subDir ) ) ;
88+ resolve ( interpreters ) ;
89+ } ) ;
90+ } ) ;
91+ }
92+ function lookForInterpretersInVirtualEnvs ( pathToCheck : string ) : Promise < PythonPathSuggestion [ ] > {
93+ return new Promise < PythonPathSuggestion [ ] > ( resolve => {
94+ // Now look for Interpreters in this directory
95+ fs . readdir ( pathToCheck , ( err , subDirs ) => {
96+ if ( err ) {
97+ return resolve ( [ ] ) ;
98+ }
99+ const envsInterpreters = [ ] ;
100+ const promises = subDirs . map ( subDir => {
101+ subDir = path . join ( pathToCheck , subDir ) ;
102+ const interpreterFolder = utils . IS_WINDOWS ? path . join ( subDir , 'scripts' ) : path . join ( subDir , 'bin' ) ;
103+ return lookForInterpretersInPath ( interpreterFolder ) ;
104+ } ) ;
105+ Promise . all < string [ ] > ( promises ) . then ( pathsWithInterpreters => {
106+ pathsWithInterpreters . forEach ( interpreters => {
107+ interpreters . map ( interpter => {
108+ envsInterpreters . push ( {
109+ label : path . basename ( interpter ) , path : interpter , type : ''
110+ } ) ;
111+ } )
112+ } ) ;
113+
114+ resolve ( envsInterpreters ) ;
115+ } )
116+ } ) ;
117+ } ) ;
118+ }
75119function suggestionsFromKnownPaths ( ) : Promise < PythonPathSuggestion [ ] > {
76120 return new Promise ( resolve => {
77- const validPaths = getSearchPaths ( ) . map ( p => utils . validatePath ( p ) ) ;
78- Promise . all < string > ( validPaths ) . then ( paths => {
79- const suggestions = paths . filter ( p => p . length > 0 ) . map ( p => {
80- return < PythonPathSuggestion > {
81- label : path . basename ( p ) , path : p , type : ''
121+ const validPaths = getSearchPaths ( ) . map ( p => {
122+ return utils . validatePath ( p ) . then ( validatedPath => {
123+ if ( validatedPath . length === 0 ) {
124+ return Promise . resolve < string [ ] > ( [ ] ) ;
82125 }
126+
127+ return lookForInterpretersInPath ( validatedPath ) ;
128+ } ) ;
129+ } ) ;
130+ Promise . all < string [ ] > ( validPaths ) . then ( listOfInterpreters => {
131+ const suggestions : PythonPathSuggestion [ ] = [ ] ;
132+ listOfInterpreters . forEach ( interpreters => {
133+ interpreters . filter ( interpter => interpter . length > 0 ) . map ( interpter => {
134+ suggestions . push ( {
135+ label : path . basename ( interpter ) , path : interpter , type : ''
136+ } ) ;
137+ } ) ;
83138 } ) ;
84139 resolve ( suggestions ) ;
85140 } ) ;
@@ -116,23 +171,31 @@ function suggestionsFromConda(): Promise<PythonPathSuggestion[]> {
116171 } ) ;
117172}
118173
119- function suggestionToQuickPickItem ( suggestion : PythonPathSuggestion ) : vscode . QuickPickItem {
174+
175+ function suggestionToQuickPickItem ( suggestion : PythonPathSuggestion ) : PythonPathQuickPickItem {
176+ let detail = suggestion . path ;
177+ if ( suggestion . path . startsWith ( vscode . workspace . rootPath ) ) {
178+ detail = path . relative ( vscode . workspace . rootPath , suggestion . path ) ;
179+ }
180+ detail = utils . IS_WINDOWS ? detail . replace ( / \\ / g, "/" ) : detail ;
120181 return {
121182 label : suggestion . label ,
122183 description : suggestion . type ,
123- detail : utils . IS_WINDOWS ? suggestion . path . replace ( / \\ / g, "/" ) : suggestion . path
184+ detail : detail ,
185+ path : utils . IS_WINDOWS ? suggestion . path . replace ( / \\ / g, "/" ) : suggestion . path
124186 }
125187}
126188
127- function suggestPythonPaths ( ) : Promise < vscode . QuickPickItem [ ] > {
189+ function suggestPythonPaths ( ) : Promise < PythonPathQuickPickItem [ ] > {
128190 // For now we only interrogate conda for suggestions.
129191 const condaSuggestions = suggestionsFromConda ( ) ;
130192 const knownPathSuggestions = suggestionsFromKnownPaths ( ) ;
193+ const virtualEnvSuggestions = lookForInterpretersInVirtualEnvs ( vscode . workspace . rootPath ) ;
131194
132195 // Here we could also look for virtualenvs/default install locations...
133196
134- return Promise . all < PythonPathSuggestion [ ] > ( [ condaSuggestions , knownPathSuggestions ] ) . then ( suggestions => {
135- const quickPicks : vscode . QuickPickItem [ ] = [ ] ;
197+ return Promise . all < PythonPathSuggestion [ ] > ( [ condaSuggestions , knownPathSuggestions , virtualEnvSuggestions ] ) . then ( suggestions => {
198+ const quickPicks : PythonPathQuickPickItem [ ] = [ ] ;
136199 suggestions . forEach ( list => {
137200 quickPicks . push ( ...list . map ( suggestionToQuickPickItem ) ) ;
138201 } ) ;
@@ -201,7 +264,7 @@ function presentQuickPickOfSuggestedPythonPaths() {
201264 vscode . window . showQuickPick ( suggestions , quickPickOptions ) . then (
202265 value => {
203266 if ( value !== undefined ) {
204- setPythonPath ( value . detail ) ;
267+ setPythonPath ( value . path ) ;
205268 }
206269 } ) ;
207270 } ) ;
0 commit comments