Skip to content

Commit a019d44

Browse files
authored
#1228 support multi roots in language service (#1309)
* update to use latest api * config changes for multiroot workspace * linting support with multi roots * multi root support for formatters * determine workspace root path * revert change * support multiple configs per workspace folder * modify formatters to use resource specific settings * modified installer to pass resource for workspace resolution * null test in installer * canges to config settings to support multiroot workspace * changes to code refactoring to support workspace symbols * oops * modified to settings are resolved using document uri * unit tests for multi root support * fix unittests for multiroot * exclude files * add new line * config changes for multiroot workspace * new lines and enabled multi root linter tests * fix sys variables * added unit test to resolve ${workspaceRoot} in settings.json * #1228 workspace symbols with multiroot support * fix test * added some data for workspace symbol tests * data for unit tests * fixed to add support for multit roots with unit tests * account for mutiroot files in sub directory * disable all but multiroot tests * fixed tests * fix tests * test where failing * properly determine root workspace * fix pytest unit test * delete files * add awaiter * use a path that works on multiple os * fixes * uncomment * invert * debug statements * use default workspace * reverted unwanted changes * oops * test unittests only * more logging * partial fixes to unit tests * run all tests * changes not to set paths for shebang tests * remove comments * update settings only if necessary * fix test * include files for tests * Fixed travis tests for multi root workspace symbols (#1306) * added logging * more logging * yay * fixed * more fixes * fix tests * removed logging * enable all tests * uncommented * Added brackets around print statements (for p3) * use resource when getting settings * support multiroot in language services
1 parent 3457c62 commit a019d44

File tree

16 files changed

+486
-465
lines changed

16 files changed

+486
-465
lines changed

src/client/extension.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
import * as vscode from 'vscode';
4+
import { JediFactory } from './languageServices/jediProxyFactory';
45
import { PythonCompletionItemProvider } from './providers/completionProvider';
56
import { PythonHoverProvider } from './providers/hoverProvider';
67
import { PythonDefinitionProvider } from './providers/definitionProvider';
@@ -68,7 +69,8 @@ export async function activate(context: vscode.ExtensionContext) {
6869
context.subscriptions.push(activateUpdateSparkLibraryProvider());
6970
activateSimplePythonRefactorProvider(context, formatOutChannel);
7071
context.subscriptions.push(activateFormatOnSaveProvider(PYTHON, formatOutChannel));
71-
context.subscriptions.push(activateGoToObjectDefinitionProvider(context));
72+
const jediFactory = new JediFactory(context.asAbsolutePath('.'));
73+
context.subscriptions.push(...activateGoToObjectDefinitionProvider(jediFactory));
7274

7375
context.subscriptions.push(vscode.commands.registerCommand(Commands.Start_REPL, () => {
7476
getPathFromPythonCommand(["-c", "import sys;print(sys.executable)"]).catch(() => {
@@ -99,19 +101,19 @@ export async function activate(context: vscode.ExtensionContext) {
99101
]
100102
});
101103

104+
context.subscriptions.push(jediFactory);
102105
context.subscriptions.push(vscode.languages.registerRenameProvider(PYTHON, new PythonRenameProvider(formatOutChannel)));
103-
const definitionProvider = new PythonDefinitionProvider(context);
104-
const jediProx = definitionProvider.JediProxy;
106+
const definitionProvider = new PythonDefinitionProvider(jediFactory);
105107
context.subscriptions.push(vscode.languages.registerDefinitionProvider(PYTHON, definitionProvider));
106-
context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(context, jediProx)));
107-
context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(context, jediProx)));
108-
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(context, jediProx), '.'));
108+
context.subscriptions.push(vscode.languages.registerHoverProvider(PYTHON, new PythonHoverProvider(jediFactory)));
109+
context.subscriptions.push(vscode.languages.registerReferenceProvider(PYTHON, new PythonReferenceProvider(jediFactory)));
110+
context.subscriptions.push(vscode.languages.registerCompletionItemProvider(PYTHON, new PythonCompletionItemProvider(jediFactory), '.'));
109111
context.subscriptions.push(vscode.languages.registerCodeLensProvider(PYTHON, new ShebangCodeLensProvider()))
110112

111-
const symbolProvider = new PythonSymbolProvider(context, jediProx);
113+
const symbolProvider = new PythonSymbolProvider(jediFactory);
112114
context.subscriptions.push(vscode.languages.registerDocumentSymbolProvider(PYTHON, symbolProvider));
113115
if (pythonSettings.devOptions.indexOf('DISABLE_SIGNATURE') === -1) {
114-
context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(context, jediProx), '(', ','));
116+
context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(PYTHON, new PythonSignatureProvider(jediFactory), '(', ','));
115117
}
116118
if (pythonSettings.formatting.provider !== 'none') {
117119
const formatProvider = new PythonFormattingEditProvider(context, formatOutChannel);

src/client/jedi/main.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"use strict";
22

3-
import { SocketClient } from './socketClient';
4-
import { SocketServer } from '../common/comms/socketServer';
53
import * as child_process from 'child_process';
64
import * as path from 'path';
75
import * as vscode from 'vscode';
6+
import { SocketClient } from './socketClient';
7+
import { SocketServer } from '../common/comms/socketServer';
88
import { createDeferred, Deferred } from '../common/helpers';
99
import { PythonSettings } from '../common/configSettings';
1010
import { EventEmitter } from 'events';
@@ -80,7 +80,8 @@ export class ClientAdapter extends EventEmitter {
8080
this.startSocketServer().then(port => {
8181
const def = createDeferred<any>();
8282
const options = { env: newEnv, cwd: this.rootDir };
83-
this.process = child_process.spawn(PythonSettings.getInstance().pythonPath, [pyFile, port.toString()], options);
83+
const rootDirUri = this.rootDir ? vscode.Uri.file(this.rootDir) : undefined;
84+
this.process = child_process.spawn(PythonSettings.getInstance(rootDirUri).pythonPath, [pyFile, port.toString()], options);
8485
this.process.stdout.setEncoding('utf8');
8586
this.process.stderr.setEncoding('utf8');
8687

src/client/jedi/parsers/CompletionParser.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { CompletionItem, SymbolKind, SnippetString } from 'vscode';
21
import * as proxy from '../../providers/jediProxy';
32
import { extractSignatureAndDocumentation } from '../../providers/jediHelpers';
43
import { PythonSettings } from '../../common/configSettings';
5-
6-
const pythonSettings = PythonSettings.getInstance();
4+
import { CompletionItem, SymbolKind, SnippetString, Uri } from 'vscode';
75

86
export class CompletionParser {
9-
public static parse(data: proxy.ICompletionResult): CompletionItem[] {
7+
public static parse(data: proxy.ICompletionResult, resource: Uri): CompletionItem[] {
108
if (!data || data.items.length === 0) {
119
return [];
1210
}
@@ -16,7 +14,7 @@ export class CompletionParser {
1614
completionItem.kind = item.type;
1715
completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1];
1816
completionItem.detail = sigAndDocs[0].split(/\r?\n/).join('');
19-
if (pythonSettings.autoComplete.addBrackets === true &&
17+
if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true &&
2018
(item.kind === SymbolKind.Function || item.kind === SymbolKind.Method)) {
2119
completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")");
2220
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Disposable, Uri, workspace } from 'vscode';
2+
import { JediProxy, JediProxyHandler, ICommandResult } from '../providers/jediProxy';
3+
4+
export class JediFactory implements Disposable {
5+
private disposables: Disposable[];
6+
private jediProxyHandlers: Map<string, JediProxyHandler<ICommandResult>>;
7+
8+
constructor(private extensionRootPath: string) {
9+
this.disposables = [];
10+
this.jediProxyHandlers = new Map<string, JediProxyHandler<ICommandResult>>();
11+
}
12+
13+
public dispose() {
14+
this.disposables.forEach(disposable => disposable.dispose());
15+
this.disposables = [];
16+
}
17+
public getJediProxyHandler<T extends ICommandResult>(resource: Uri): JediProxyHandler<T> {
18+
const workspaceFolder = workspace.getWorkspaceFolder(resource);
19+
let workspacePath = workspaceFolder ? workspaceFolder.uri.fsPath : undefined;
20+
if (!workspacePath) {
21+
if (Array.isArray(workspace.workspaceFolders) && workspace.workspaceFolders.length > 0) {
22+
workspacePath = workspace.workspaceFolders[0].uri.fsPath;
23+
}
24+
else {
25+
workspacePath = __dirname;
26+
}
27+
}
28+
29+
if (!this.jediProxyHandlers.has(workspacePath)) {
30+
const jediProxy = new JediProxy(this.extensionRootPath, workspacePath);
31+
const jediProxyHandler = new JediProxyHandler(jediProxy);
32+
this.disposables.push(jediProxy, jediProxyHandler);
33+
this.jediProxyHandlers.set(workspacePath, jediProxyHandler);
34+
}
35+
// tslint:disable-next-line:no-non-null-assertion
36+
return this.jediProxyHandlers.get(workspacePath)! as JediProxyHandler<T>;
37+
}
38+
}

src/client/providers/completionProvider.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,21 @@ import * as telemetryContracts from '../common/telemetryContracts';
77
import { extractSignatureAndDocumentation } from './jediHelpers';
88
import { EOL } from 'os';
99
import { PythonSettings } from '../common/configSettings';
10-
import { SnippetString } from 'vscode';
11-
12-
const pythonSettings = PythonSettings.getInstance();
10+
import { SnippetString, Uri } from 'vscode';
11+
import { JediFactory } from '../languageServices/jediProxyFactory';
1312

1413
export class PythonCompletionItemProvider implements vscode.CompletionItemProvider {
15-
private jediProxyHandler: proxy.JediProxyHandler<proxy.ICompletionResult>;
1614

17-
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
18-
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);
19-
}
20-
private static parseData(data: proxy.ICompletionResult): vscode.CompletionItem[] {
15+
public constructor(private jediFactory: JediFactory) { }
16+
private static parseData(data: proxy.ICompletionResult, resource: Uri): vscode.CompletionItem[] {
2117
if (data && data.items.length > 0) {
2218
return data.items.map(item => {
2319
const sigAndDocs = extractSignatureAndDocumentation(item);
2420
let completionItem = new vscode.CompletionItem(item.text);
2521
completionItem.kind = item.type;
2622
completionItem.documentation = sigAndDocs[1].length === 0 ? item.description : sigAndDocs[1];
2723
completionItem.detail = sigAndDocs[0].split(/\r?\n/).join('');
28-
if (pythonSettings.autoComplete.addBrackets === true &&
24+
if (PythonSettings.getInstance(resource).autoComplete.addBrackets === true &&
2925
(item.kind === vscode.SymbolKind.Function || item.kind === vscode.SymbolKind.Method)) {
3026
completionItem.insertText = new SnippetString(item.text).appendText("(").appendTabstop().appendText(")");
3127
}
@@ -67,10 +63,10 @@ export class PythonCompletionItemProvider implements vscode.CompletionItemProvid
6763
};
6864

6965
const timer = new telemetryHelper.Delays();
70-
return this.jediProxyHandler.sendCommand(cmd, token).then(data => {
66+
return this.jediFactory.getJediProxyHandler<proxy.ICompletionResult>(document.uri).sendCommand(cmd, token).then(data => {
7167
timer.stop();
7268
telemetryHelper.sendTelemetryEvent(telemetryContracts.IDE.Completion, {}, timer.toMeasures());
73-
const completions = PythonCompletionItemProvider.parseData(data);
69+
const completions = PythonCompletionItemProvider.parseData(data, document.uri);
7470
return completions;
7571
});
7672
}

src/client/providers/definitionProvider.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,11 @@
22

33
import * as vscode from 'vscode';
44
import * as proxy from './jediProxy';
5-
import * as telemetryContracts from "../common/telemetryContracts";
5+
import * as telemetryContracts from '../common/telemetryContracts';
6+
import { JediFactory } from '../languageServices/jediProxyFactory';
67

78
export class PythonDefinitionProvider implements vscode.DefinitionProvider {
8-
private jediProxyHandler: proxy.JediProxyHandler<proxy.IDefinitionResult>;
9-
public get JediProxy(): proxy.JediProxy {
10-
return this.jediProxyHandler.JediProxy;
11-
}
12-
13-
public constructor(context: vscode.ExtensionContext) {
14-
this.jediProxyHandler = new proxy.JediProxyHandler(context);
15-
}
9+
public constructor(private jediFactory: JediFactory) { }
1610
private static parseData(data: proxy.IDefinitionResult, possibleWord: string): vscode.Definition {
1711
if (data && Array.isArray(data.definitions) && data.definitions.length > 0) {
1812
const definitions = data.definitions.filter(d => d.text === possibleWord);
@@ -46,7 +40,7 @@ export class PythonDefinitionProvider implements vscode.DefinitionProvider {
4640
cmd.source = document.getText();
4741
}
4842
let possibleWord = document.getText(range);
49-
return this.jediProxyHandler.sendCommand(cmd, token).then(data => {
43+
return this.jediFactory.getJediProxyHandler<proxy.IDefinitionResult>(document.uri).sendCommand(cmd, token).then(data => {
5044
return PythonDefinitionProvider.parseData(data, possibleWord);
5145
});
5246
}

src/client/providers/hoverProvider.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ import * as vscode from 'vscode';
44
import * as proxy from './jediProxy';
55
import { highlightCode } from './jediHelpers';
66
import { EOL } from 'os';
7+
import { JediFactory } from '../languageServices/jediProxyFactory';
78

89
export class PythonHoverProvider implements vscode.HoverProvider {
9-
private jediProxyHandler: proxy.JediProxyHandler<proxy.IHoverResult>;
10-
11-
public constructor(context: vscode.ExtensionContext, jediProxy: proxy.JediProxy = null) {
12-
this.jediProxyHandler = new proxy.JediProxyHandler(context, jediProxy);
13-
}
10+
public constructor(private jediFactory: JediFactory) { }
1411
private static parseData(data: proxy.IHoverResult, currentWord: string): vscode.Hover {
1512
let results = [];
1613
let capturedInfo: string[] = [];
@@ -96,7 +93,7 @@ export class PythonHoverProvider implements vscode.HoverProvider {
9693
cmd.source = document.getText();
9794
}
9895

99-
const data = await this.jediProxyHandler.sendCommand(cmd, token);
96+
const data = await this.jediFactory.getJediProxyHandler<proxy.IHoverResult>(document.uri).sendCommand(cmd, token);
10097
if (!data || !data.items.length) {
10198
return;
10299
}

0 commit comments

Comments
 (0)