Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ee2f424
Upstream Code OSS changes from 1.104.0 to 1.105.0
positron-bot[bot] Oct 16, 2025
a6b6a6f
regen reh-web lockfile
positron-bot[bot] Oct 16, 2025
c57061a
remove uuid dependency in reticulate
positron-bot[bot] Oct 16, 2025
a34a429
regen standalone enums
positron-bot[bot] Oct 16, 2025
9d35f4c
use node module resolution in positron-proxy
positron-bot[bot] Oct 16, 2025
cb6b7a6
fix remaining extension compilation issues
positron-bot[bot] Oct 16, 2025
6f23f8f
adapt to variety of chat related refactors
jmcphers Oct 17, 2025
1da2aff
use modern node module resolution
jmcphers Oct 17, 2025
648d705
fix typescript strictness & module resolution
jmcphers Oct 18, 2025
0152302
fix positron-r install-kernel compilation
jmcphers Oct 21, 2025
3e0620a
fix mocha type imports in positron-r
jmcphers Oct 21, 2025
ac3b1bd
more type import issues in positron-r
jmcphers Oct 21, 2025
1d436d1
suppress remote coding agent ui
jmcphers Oct 21, 2025
be70f21
bump copilot-chat to 1.105
jmcphers Oct 21, 2025
dd6f42b
fix one more positron-r import
jmcphers Oct 21, 2025
5a7048e
Merge remote-tracking branch 'origin/main' into merge/1.105.0
jmcphers Oct 21, 2025
dfd72ab
regenerate codicons
jmcphers Oct 21, 2025
baa62c9
remove unused workflow from upstream
jmcphers Oct 22, 2025
88ccac4
Merge remote-tracking branch 'origin/main' into merge/1.105.0
jmcphers Oct 22, 2025
c5cba6b
update import for web-tree-sitter for test parser
jmcphers Oct 22, 2025
ae7d784
don't show upstream copilot signup in positron
jmcphers Oct 22, 2025
0ff4369
Merge remote-tracking branch 'origin/main' into merge/1.105.0
jmcphers Oct 22, 2025
6a530bc
Merge remote-tracking branch 'origin/main' into merge/1.105.0
jmcphers Oct 23, 2025
29074d0
Merge remote-tracking branch 'origin/main' into merge/1.105.0
jmcphers Oct 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
76 changes: 0 additions & 76 deletions .config/guardian/.gdnsuppress

This file was deleted.

1 change: 1 addition & 0 deletions .eslint-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
**/extensions/markdown-language-features/media/**
**/extensions/markdown-language-features/notebook-out/**
**/extensions/markdown-math/notebook-out/**
**/extensions/mermaid-chat-features/chat-webview-out/**
**/extensions/notebook-renderers/renderer-out/index.js
**/extensions/simple-browser/media/index.js
**/extensions/terminal-suggest/src/completions/upstream/**
Expand Down
145 changes: 145 additions & 0 deletions .eslint-plugin-local/code-no-observable-get-in-reactive-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as eslint from 'eslint';
import { TSESTree } from '@typescript-eslint/utils';
import * as ESTree from 'estree';
import * as visitorKeys from 'eslint-visitor-keys';

export = new class NoObservableGetInReactiveContext implements eslint.Rule.RuleModule {
meta: eslint.Rule.RuleMetaData = {
type: 'problem',
docs: {
description: 'Disallow calling .get() on observables inside reactive contexts in favor of .read(undefined).',
},
fixable: 'code',
};

create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
return {
'CallExpression': (node: any) => {
const callExpression = node as TSESTree.CallExpression;

if (!isReactiveFunctionWithReader(callExpression.callee)) {
return;
}

const functionArg = callExpression.arguments.find(arg =>
arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression'
) as TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | undefined;

if (!functionArg) {
return;
}

const readerName = getReaderParameterName(functionArg);
if (!readerName) {
return;
}

checkFunctionForObservableGetCalls(functionArg, readerName, context);
}
};
}
};

function checkFunctionForObservableGetCalls(
fn: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
readerName: string,
context: eslint.Rule.RuleContext
) {
const visited = new Set<TSESTree.Node>();

function traverse(node: TSESTree.Node) {
if (visited.has(node)) {
return;
}
visited.add(node);

if (node.type === 'CallExpression' && isObservableGetCall(node)) {
// Flag .get() calls since we're always in a reactive context here
context.report({
node: node as any as ESTree.Node,
message: `Observable '.get()' should not be used in reactive context. Use '.read(${readerName})' instead to properly track dependencies or '.read(undefined)' to be explicit about an untracked read.`,
fix: (fixer) => {
const memberExpression = node.callee as TSESTree.MemberExpression;
return fixer.replaceText(node as any, `${context.getSourceCode().getText(memberExpression.object as any)}.read(undefined)`);
}
});
}

walkChildren(node, traverse);
}

if (fn.body) {
traverse(fn.body);
}
}

function isObservableGetCall(node: TSESTree.CallExpression): boolean {
// Look for pattern: something.get()
if (node.callee.type === 'MemberExpression' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'get' &&
node.arguments.length === 0) {

// This is a .get() call with no arguments, which is likely an observable
return true;
}
return false;
}

const reactiveFunctions = new Set([
'derived',
'derivedDisposable',
'derivedHandleChanges',
'derivedOpts',
'derivedWithSetter',
'derivedWithStore',
'autorun',
'autorunOpts',
'autorunHandleChanges',
'autorunSelfDisposable',
'autorunDelta',
'autorunWithStore',
'autorunWithStoreHandleChanges',
'autorunIterableDelta'
]);

function getReaderParameterName(fn: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression): string | null {
if (fn.params.length === 0) {
return null;
}
const firstParam = fn.params[0];
if (firstParam.type === 'Identifier') {
// Accept any parameter name as a potential reader parameter
// since reactive functions should always have the reader as the first parameter
return firstParam.name;
}
return null;
}

function isReactiveFunctionWithReader(callee: TSESTree.Node): boolean {
if (callee.type === 'Identifier') {
return reactiveFunctions.has(callee.name);
}
return false;
}

function walkChildren(node: TSESTree.Node, cb: (child: TSESTree.Node) => void) {
const keys = visitorKeys.KEYS[node.type] || [];
for (const key of keys) {
const child = (node as any)[key];
if (Array.isArray(child)) {
for (const item of child) {
if (item && typeof item === 'object' && item.type) {
cb(item);
}
}
} else if (child && typeof child === 'object' && child.type) {
cb(child);
}
}
}
168 changes: 168 additions & 0 deletions .eslint-plugin-local/code-no-reader-after-await.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as eslint from 'eslint';
import { TSESTree } from '@typescript-eslint/utils';
import * as ESTree from 'estree';

export = new class NoReaderAfterAwait implements eslint.Rule.RuleModule {
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
return {
'CallExpression': (node: any) => {
const callExpression = node as TSESTree.CallExpression;

if (!isFunctionWithReader(callExpression.callee)) {
return;
}

const functionArg = callExpression.arguments.find(arg =>
arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression'
) as TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | undefined;

if (!functionArg) {
return;
}

const readerName = getReaderParameterName(functionArg);
if (!readerName) {
return;
}

checkFunctionForAwaitBeforeReader(functionArg, readerName, context);
}
};
}
};

function checkFunctionForAwaitBeforeReader(
fn: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression,
readerName: string,
context: eslint.Rule.RuleContext
) {
const awaitPositions: { line: number; column: number }[] = [];
const visited = new Set<TSESTree.Node>();

function collectPositions(node: TSESTree.Node) {
if (visited.has(node)) {
return;
}
visited.add(node);

if (node.type === 'AwaitExpression') {
awaitPositions.push({
line: node.loc?.start.line || 0,
column: node.loc?.start.column || 0
});
} else if (node.type === 'CallExpression' && isReaderMethodCall(node, readerName)) {
if (awaitPositions.length > 0) {
const methodName = getMethodName(node);
context.report({
node: node as any as ESTree.Node,
message: `Reader method '${methodName}' should not be called after 'await'. The reader becomes invalid after async operations.`
});
}
}

// Safely traverse known node types only
switch (node.type) {
case 'BlockStatement':
node.body.forEach(stmt => collectPositions(stmt));
break;
case 'ExpressionStatement':
collectPositions(node.expression);
break;
case 'VariableDeclaration':
node.declarations.forEach(decl => {
if (decl.init) { collectPositions(decl.init); }
});
break;
case 'AwaitExpression':
if (node.argument) { collectPositions(node.argument); }
break;
case 'CallExpression':
node.arguments.forEach(arg => collectPositions(arg));
break;
case 'IfStatement':
collectPositions(node.test);
collectPositions(node.consequent);
if (node.alternate) { collectPositions(node.alternate); }
break;
case 'TryStatement':
collectPositions(node.block);
if (node.handler) { collectPositions(node.handler.body); }
if (node.finalizer) { collectPositions(node.finalizer); }
break;
case 'ReturnStatement':
if (node.argument) { collectPositions(node.argument); }
break;
case 'BinaryExpression':
case 'LogicalExpression':
collectPositions(node.left);
collectPositions(node.right);
break;
case 'MemberExpression':
collectPositions(node.object);
if (node.computed) { collectPositions(node.property); }
break;
case 'AssignmentExpression':
collectPositions(node.left);
collectPositions(node.right);
break;
}
}

if (fn.body) {
collectPositions(fn.body);
}
}

function getMethodName(callExpression: TSESTree.CallExpression): string {
if (callExpression.callee.type === 'MemberExpression' &&
callExpression.callee.property.type === 'Identifier') {
return callExpression.callee.property.name;
}
return 'read';
}

function isReaderMethodCall(node: TSESTree.CallExpression, readerName: string): boolean {
if (node.callee.type === 'MemberExpression') {
// Pattern 1: reader.read() or reader.readObservable()
if (node.callee.object.type === 'Identifier' &&
node.callee.object.name === readerName &&
node.callee.property.type === 'Identifier') {
return ['read', 'readObservable'].includes(node.callee.property.name);
}

// Pattern 2: observable.read(reader) or observable.readObservable(reader)
if (node.callee.property.type === 'Identifier' &&
['read', 'readObservable'].includes(node.callee.property.name)) {
// Check if the reader is passed as the first argument
return node.arguments.length > 0 &&
node.arguments[0].type === 'Identifier' &&
node.arguments[0].name === readerName;
}
}
return false;
}

const readerFunctions = new Set(['derived', 'autorun', 'autorunOpts', 'autorunHandleChanges', 'autorunSelfDisposable']);

function getReaderParameterName(fn: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression): string | null {
if (fn.params.length === 0) {
return null;
}
const firstParam = fn.params[0];
if (firstParam.type === 'Identifier') {
return firstParam.name;
}
return null;
}

function isFunctionWithReader(callee: TSESTree.Node): boolean {
if (callee.type === 'Identifier') {
return readerFunctions.has(callee.name);
}
return false;
}
Loading