Skip to content

Commit 2d5e616

Browse files
seeMwesm
authored andcommitted
Merged PR posit-dev/positron-python#259: Use "smart send" for Cmd+Enter statement range detection
Merge pull request #259 from posit-dev/smart-send Use "smart send" for Cmd+Enter statement range detection -------------------- Commit message for posit-dev/positron-python@8a7f634: fix merge issues -------------------- Commit message for posit-dev/positron-python@a6ead38: Merge remote-tracking branch 'origin/main' into smart-send -------------------- Commit message for posit-dev/positron-python@96f3ed2: fix excluding closing token Addresses #1811. -------------------- Commit message for posit-dev/positron-python@7872ce0: use smart send feature in positron statement range provider Authored-by: Wasim Lorgat <[email protected]> Signed-off-by: Wasim Lorgat <[email protected]>
1 parent 6d6cc0c commit 2d5e616

File tree

6 files changed

+184
-79
lines changed

6 files changed

+184
-79
lines changed

extensions/positron-python/pythonFiles/normalizeSelection.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,7 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted):
181181
ast.IfExp,
182182
ast.ExceptHandler,
183183
)
184-
if isinstance(node, ast_types_with_nodebody) and isinstance(
185-
node.body, Iterable
186-
):
184+
if isinstance(node, ast_types_with_nodebody) and isinstance(node.body, Iterable):
187185
for child_nodes in node.body:
188186
top_level_nodes.append(child_nodes)
189187

@@ -194,13 +192,18 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted):
194192
which_line_next = 0
195193
for same_line_node in exact_nodes:
196194
should_run_top_blocks.append(same_line_node)
197-
smart_code += (
198-
f"{ast.get_source_segment(wholeFileContent, same_line_node)}\n"
199-
)
195+
smart_code += f"{ast.get_source_segment(wholeFileContent, same_line_node)}\n"
200196
which_line_next = get_next_block_lineno(should_run_top_blocks)
201197
return {
202198
"normalized_smart_result": smart_code,
203199
"which_line_next": which_line_next,
200+
# --- Start Positron ---
201+
# Return additional info required by a Positron statement range provider.
202+
"start_line": should_run_top_blocks[0].lineno,
203+
"start_character": should_run_top_blocks[0].col_offset,
204+
"end_line": should_run_top_blocks[-1].end_lineno,
205+
"end_character": should_run_top_blocks[-1].end_col_offset,
206+
# --- End Positron ---
204207
}
205208

206209
# For each of the nodes in the parsed file content,
@@ -228,11 +231,28 @@ def traverse_file(wholeFileContent, start_line, end_line, was_highlighted):
228231
smart_code += str(ast.get_source_segment(wholeFileContent, top_node))
229232
smart_code += "\n"
230233

234+
# --- Start Positron ---
235+
# If we get here, we may still be between top-level nodes -- try to find the next one.
236+
if not should_run_top_blocks:
237+
for node in top_level_nodes:
238+
if node.lineno > start_line:
239+
should_run_top_blocks.append(node)
240+
smart_code += f"{ast.get_source_segment(wholeFileContent, top_node)}\n"
241+
break
242+
# --- End Positron ---
243+
231244
normalized_smart_result = normalize_lines(smart_code)
232245
which_line_next = get_next_block_lineno(should_run_top_blocks)
233246
return {
234247
"normalized_smart_result": normalized_smart_result,
235248
"which_line_next": which_line_next,
249+
# --- Start Positron ---
250+
# Return additional info required by a Positron statement range provider.
251+
"start_line": should_run_top_blocks[0].lineno,
252+
"start_character": should_run_top_blocks[0].col_offset,
253+
"end_line": should_run_top_blocks[-1].end_lineno,
254+
"end_character": should_run_top_blocks[-1].end_col_offset,
255+
# --- End Positron ---
236256
}
237257

238258

@@ -277,7 +297,17 @@ def get_next_block_lineno(which_line_next):
277297
normalized = result["normalized_smart_result"]
278298
which_line_next = result["which_line_next"]
279299
data = json.dumps(
280-
{"normalized": normalized, "nextBlockLineno": result["which_line_next"]}
300+
# --- Start Positron ---
301+
# Return additional info required by a Positron statement range provider.
302+
{
303+
"normalized": normalized,
304+
"nextBlockLineno": result["which_line_next"],
305+
"startLine": result["start_line"],
306+
"endLine": result["end_line"],
307+
"startCharacter": result["start_character"],
308+
"endCharacter": result["end_character"],
309+
}
310+
# --- End Positron ---
281311
)
282312
else:
283313
normalized = normalize_lines(contents["code"])

extensions/positron-python/src/client/positron/extension.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { IInterpreterService } from '../interpreter/contracts';
1010
import { IServiceContainer } from '../ioc/types';
1111
import { traceError, traceInfo } from '../logging';
1212
import { createPythonRuntime, pythonRuntimeProvider } from './provider';
13-
import { PythonStatementRangeProvider } from './statementRange';
1413

1514
export async function activatePositron(
1615
activatedPromise: Promise<void>,
@@ -29,10 +28,6 @@ export async function activatePositron(
2928
pythonRuntimeProvider(serviceContainer, runtimes, activatedPromise),
3029
);
3130

32-
// Register a statement range provider to detect Python statements
33-
traceInfo('activatePositron: registering python statement range provider');
34-
positron.languages.registerStatementRangeProvider('python', new PythonStatementRangeProvider());
35-
3631
// Wait for all extension components to be activated before registering event listeners
3732
traceInfo('activatePositron: awaiting extension activation');
3833
await activatedPromise;

extensions/positron-python/src/client/positron/lsp.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { LanguageClient, LanguageClientOptions, State, StreamInfo } from 'vscode
88
import { Socket } from 'net';
99

1010
import { PYTHON_LANGUAGE } from '../common/constants';
11+
import { IServiceContainer } from '../ioc/types';
1112
import { traceError, traceInfo } from '../logging';
1213
import { ProgressReporting } from '../activation/progress';
1314
import { PromiseHandles } from './util';
@@ -40,10 +41,11 @@ export class PythonLsp implements vscode.Disposable {
4041
private activationDisposables: vscode.Disposable[] = [];
4142

4243
public constructor(
44+
private readonly serviceContainer: IServiceContainer,
4345
private readonly _version: string,
4446
private readonly _clientOptions: LanguageClientOptions,
4547
private readonly _notebook: vscode.NotebookDocument | undefined,
46-
) { }
48+
) {}
4749

4850
/**
4951
* Activate the language server; returns a promise that resolves when the LSP is
@@ -82,10 +84,10 @@ export class PythonLsp implements vscode.Disposable {
8284
this._clientOptions.documentSelector = this._notebook
8385
? [{ language: 'python', pattern: this._notebook.uri.path }]
8486
: [
85-
{ language: 'python', scheme: 'untitled' },
86-
{ language: 'python', scheme: 'inmemory' }, // Console
87-
{ language: 'python', pattern: '**/*.py' },
88-
];
87+
{ language: 'python', scheme: 'untitled' },
88+
{ language: 'python', scheme: 'inmemory' }, // Console
89+
{ language: 'python', pattern: '**/*.py' },
90+
];
8991

9092
traceInfo(`Creating Positron Python ${this._version} language client (port ${port})...`);
9193
this._client = new LanguageClient(
@@ -165,20 +167,20 @@ export class PythonLsp implements vscode.Disposable {
165167

166168
const promise = awaitStop
167169
? // If the kernel hasn't exited, we can just await the promise directly
168-
this._client!.stop()
170+
this._client!.stop()
169171
: // The promise returned by `stop()` never resolves if the server
170-
// side is disconnected, so rather than awaiting it when the runtime
171-
// has exited, we wait for the client to change state to `stopped`,
172-
// which does happen reliably.
173-
new Promise<void>((resolve) => {
174-
const disposable = this._client!.onDidChangeState((event) => {
175-
if (event.newState === State.Stopped) {
176-
resolve();
177-
disposable.dispose();
178-
}
179-
});
180-
this._client!.stop();
181-
});
172+
// side is disconnected, so rather than awaiting it when the runtime
173+
// has exited, we wait for the client to change state to `stopped`,
174+
// which does happen reliably.
175+
new Promise<void>((resolve) => {
176+
const disposable = this._client!.onDidChangeState((event) => {
177+
if (event.newState === State.Stopped) {
178+
resolve();
179+
disposable.dispose();
180+
}
181+
});
182+
this._client!.stop();
183+
});
182184

183185
// Don't wait more than a couple of seconds for the client to stop.
184186
const timeout = new Promise<void>((_, reject) => {
@@ -208,17 +210,20 @@ export class PythonLsp implements vscode.Disposable {
208210
*/
209211
private registerPositronLspExtensions(client: LanguageClient) {
210212
// Register a statement range provider to detect Python statements
211-
const rangeDisposable = positron.languages.registerStatementRangeProvider('python',
212-
new PythonStatementRangeProvider());
213+
const rangeDisposable = positron.languages.registerStatementRangeProvider(
214+
'python',
215+
new PythonStatementRangeProvider(this.serviceContainer),
216+
);
213217
this.activationDisposables.push(rangeDisposable);
214218

215219
// Register a help topic provider to provide help topics for Python
216-
const helpDisposable = positron.languages.registerHelpTopicProvider('python',
217-
new PythonHelpTopicProvider(client));
220+
const helpDisposable = positron.languages.registerHelpTopicProvider(
221+
'python',
222+
new PythonHelpTopicProvider(client),
223+
);
218224
this.activationDisposables.push(helpDisposable);
219225
}
220226

221-
222227
/**
223228
* Dispose of the client instance.
224229
*/

extensions/positron-python/src/client/positron/provider.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,16 @@ export async function createPythonRuntime(
238238

239239
// Create an adapter for the kernel to fulfill the LanguageRuntime interface.
240240
traceInfo(`createPythonRuntime: creating PythonRuntime`);
241-
return new PythonRuntime(kernelSpec, metadata, dynState, languageClientOptions, interpreter, installer, extra);
241+
return new PythonRuntime(
242+
serviceContainer,
243+
kernelSpec,
244+
metadata,
245+
dynState,
246+
languageClientOptions,
247+
interpreter,
248+
installer,
249+
extra,
250+
);
242251
}
243252

244253
// Returns a sorted copy of the array of Python environments, in descending order

extensions/positron-python/src/client/positron/runtime.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { cloneDeep } from 'lodash';
1212
import PQueue from 'p-queue';
1313
import { LanguageClientOptions } from 'vscode-languageclient/node';
1414
import { InstallOptions } from '../common/installer/types';
15-
import { IInstaller, Product, InstallerResponse } from '../common/types';
15+
import { IInstaller, InstallerResponse, Product } from '../common/types';
16+
import { IServiceContainer } from '../ioc/types';
1617
import { JupyterAdapterApi, JupyterKernelSpec, JupyterLanguageRuntime, JupyterKernelExtra } from '../jupyter-adapter.d';
1718
import { traceInfo } from '../logging';
1819
import { PythonEnvironment } from '../pythonEnvironments/info';
@@ -45,6 +46,7 @@ export class PythonRuntime implements positron.LanguageRuntime, vscode.Disposabl
4546
private adapterApi?: JupyterAdapterApi;
4647

4748
constructor(
49+
private readonly serviceContainer: IServiceContainer,
4850
readonly kernelSpec: JupyterKernelSpec,
4951
readonly metadata: positron.LanguageRuntimeMetadata,
5052
readonly dynState: positron.LanguageRuntimeDynState,
@@ -54,7 +56,7 @@ export class PythonRuntime implements positron.LanguageRuntime, vscode.Disposabl
5456
readonly extra?: JupyterKernelExtra,
5557
readonly notebook?: vscode.NotebookDocument,
5658
) {
57-
this._lsp = new PythonLsp(metadata.languageVersion, languageClientOptions, notebook);
59+
this._lsp = new PythonLsp(serviceContainer, metadata.languageVersion, languageClientOptions, notebook);
5860
this._queue = new PQueue({ concurrency: 1 });
5961
this.onDidReceiveRuntimeMessage = this._messageEmitter.event;
6062
this.onDidChangeRuntimeState = this._stateEmitter.event;
@@ -285,6 +287,7 @@ export class PythonRuntime implements positron.LanguageRuntime, vscode.Disposabl
285287
clone(metadata: positron.LanguageRuntimeMetadata, notebook: vscode.NotebookDocument): positron.LanguageRuntime {
286288
const kernelSpec: JupyterKernelSpec = { ...this.kernelSpec, display_name: metadata.runtimeName };
287289
return new PythonRuntime(
290+
this.serviceContainer,
288291
kernelSpec,
289292
metadata,
290293
{ ...this.dynState },

0 commit comments

Comments
 (0)