Merge remote-tracking branch 'origin/master' into tyriar/92038

This commit is contained in:
Daniel Imms 2021-02-05 06:26:40 -08:00
commit bfa8e4e400
114 changed files with 1813 additions and 1311 deletions

View file

@ -1,8 +1,9 @@
name: "Prevent yarn.lock changes in PRs"
name: Prevent yarn.lock changes in PRs
on: [pull_request]
jobs:
main:
name: Prevent yarn.lock changes in PRs
runs-on: ubuntu-latest
steps:
- id: file_changes

View file

@ -207,7 +207,7 @@ steps:
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \
./scripts/test-integration.sh --build --tfs "Integration Tests"
displayName: Run integration tests (Electron)
timeoutInMinutes: 5
timeoutInMinutes: 10
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
@ -244,6 +244,16 @@ steps:
displayName: Run smoke tests (Electron)
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)
APP_NAME="`ls $APP_ROOT | head -n 1`"
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \
yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote
timeoutInMinutes: 5
displayName: Run smoke tests (Remote)
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |
set -e
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \

View file

@ -172,7 +172,7 @@ steps:
VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \
DISPLAY=:10 ./scripts/test-integration.sh --build --tfs "Integration Tests"
displayName: Run integration tests (Electron)
timeoutInMinutes: 5
timeoutInMinutes: 10
condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
- script: |

View file

@ -2140,6 +2140,15 @@
"highContrast": "#c74e39"
}
},
{
"id": "gitDecoration.renamedResourceForeground",
"description": "%colors.renamed%",
"defaults": {
"light": "#007100",
"dark": "#73C991",
"highContrast": "#73C991"
}
},
{
"id": "gitDecoration.untrackedResourceForeground",
"description": "%colors.untracked%",
@ -2180,9 +2189,9 @@
"id": "gitDecoration.conflictingResourceForeground",
"description": "%colors.conflict%",
"defaults": {
"light": "#6c6cc4",
"dark": "#6c6cc4",
"highContrast": "#6c6cc4"
"light": "#ad0707",
"dark": "#e4676b",
"highContrast": "#c74e39"
}
},
{

View file

@ -198,6 +198,7 @@
"colors.stageModified": "Color for modified resources which have been staged.",
"colors.stageDeleted": "Color for deleted resources which have been staged.",
"colors.deleted": "Color for deleted resources.",
"colors.renamed": "Color for renamed or copied resources.",
"colors.untracked": "Color for untracked resources.",
"colors.ignored": "Color for ignored resources.",
"colors.conflict": "Color for resources with conflicts.",

View file

@ -55,13 +55,13 @@ export class Resource implements SourceControlResourceState {
case Status.UNTRACKED: return localize('untracked', "Untracked");
case Status.IGNORED: return localize('ignored', "Ignored");
case Status.INTENT_TO_ADD: return localize('intent to add', "Intent to Add");
case Status.BOTH_DELETED: return localize('both deleted', "Both Deleted");
case Status.ADDED_BY_US: return localize('added by us', "Added By Us");
case Status.DELETED_BY_THEM: return localize('deleted by them', "Deleted By Them");
case Status.ADDED_BY_THEM: return localize('added by them', "Added By Them");
case Status.DELETED_BY_US: return localize('deleted by us', "Deleted By Us");
case Status.BOTH_ADDED: return localize('both added', "Both Added");
case Status.BOTH_MODIFIED: return localize('both modified', "Both Modified");
case Status.BOTH_DELETED: return localize('both deleted', "Conflict: Both Deleted");
case Status.ADDED_BY_US: return localize('added by us', "Conflict: Added By Us");
case Status.DELETED_BY_THEM: return localize('deleted by them', "Conflict: Deleted By Them");
case Status.ADDED_BY_THEM: return localize('added by them', "Conflict: Added By Them");
case Status.DELETED_BY_US: return localize('deleted by us', "Conflict: Deleted By Us");
case Status.BOTH_ADDED: return localize('both added', "Conflict: Both Added");
case Status.BOTH_MODIFIED: return localize('both modified', "Conflict: Both Modified");
default: return '';
}
}
@ -199,12 +199,13 @@ export class Resource implements SourceControlResourceState {
case Status.DELETED_BY_US:
return 'D';
case Status.INDEX_COPIED:
return 'C';
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.ADDED_BY_THEM:
case Status.BOTH_ADDED:
case Status.BOTH_MODIFIED:
return 'C';
return '!'; // Using ! instead of ⚠, because the latter looks really bad on windows
default:
throw new Error('Unknown git status: ' + this.type);
}
@ -223,12 +224,13 @@ export class Resource implements SourceControlResourceState {
case Status.INDEX_ADDED:
case Status.INTENT_TO_ADD:
return new ThemeColor('gitDecoration.addedResourceForeground');
case Status.INDEX_COPIED:
case Status.INDEX_RENAMED:
return new ThemeColor('gitDecoration.renamedResourceForeground');
case Status.UNTRACKED:
return new ThemeColor('gitDecoration.untrackedResourceForeground');
case Status.IGNORED:
return new ThemeColor('gitDecoration.ignoredResourceForeground');
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:
@ -246,10 +248,10 @@ export class Resource implements SourceControlResourceState {
switch (this.type) {
case Status.INDEX_MODIFIED:
case Status.MODIFIED:
case Status.INDEX_COPIED:
return 2;
case Status.IGNORED:
return 3;
case Status.INDEX_COPIED:
case Status.BOTH_DELETED:
case Status.ADDED_BY_US:
case Status.DELETED_BY_THEM:

View file

@ -22,10 +22,7 @@ export function runSelectedScript(context: vscode.ExtensionContext) {
}
let document = editor.document;
let contents = document.getText();
let selection = editor.selection;
let offset = document.offsetAt(selection.anchor);
let script = findScriptAtPosition(contents, offset);
let script = findScriptAtPosition(editor.document, contents, editor.selection.anchor);
if (script) {
runScript(context, script, document);
} else {

View file

@ -10,6 +10,7 @@ import { runSelectedScript, selectAndRunScriptFromFolder } from './commands';
import { NpmScriptsTreeDataProvider } from './npmView';
import { getPackageManager, invalidateTasksCache, NpmTaskProvider } from './tasks';
import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover';
import { NpmScriptLensProvider } from './npmScriptLens';
let treeDataProvider: NpmScriptsTreeDataProvider | undefined;
@ -62,6 +63,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
}
return '';
}));
context.subscriptions.push(new NpmScriptLensProvider());
}
function canRunNpmInCurrentWorkspace() {

View file

@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import {
CodeLens,
CodeLensProvider,
Disposable,
EventEmitter,
languages,
TextDocument,
Uri,
workspace
} from 'vscode';
import * as nls from 'vscode-nls';
import { findPreferredPM } from './preferred-pm';
import { readScripts } from './readScripts';
const localize = nls.loadMessageBundle();
const enum Constants {
ConfigKey = 'debug.javascript.codelens.npmScripts',
}
const getFreshLensLocation = () => workspace.getConfiguration().get(Constants.ConfigKey);
/**
* Npm script lens provider implementation. Can show a "Debug" text above any
* npm script, or the npm scripts section.
*/
export class NpmScriptLensProvider implements CodeLensProvider, Disposable {
private lensLocation = getFreshLensLocation();
private changeEmitter = new EventEmitter<void>();
private subscriptions: Disposable[] = [];
/**
* @inheritdoc
*/
public onDidChangeCodeLenses = this.changeEmitter.event;
constructor() {
this.subscriptions.push(
workspace.onDidChangeConfiguration(evt => {
if (evt.affectsConfiguration(Constants.ConfigKey)) {
this.lensLocation = getFreshLensLocation();
this.changeEmitter.fire();
}
}),
languages.registerCodeLensProvider(
{
language: 'json',
pattern: '**/package.json',
},
this,
)
);
}
/**
* @inheritdoc
*/
public async provideCodeLenses(document: TextDocument): Promise<CodeLens[]> {
if (this.lensLocation === 'never') {
return [];
}
const tokens = readScripts(document);
if (!tokens) {
return [];
}
const title = localize('codelens.debug', '{0} Debug', '$(debug-start)');
const cwd = path.dirname(document.uri.fsPath);
if (this.lensLocation === 'top') {
return [
new CodeLens(
tokens.location.range,
{
title,
command: 'extension.js-debug.npmScript',
arguments: [cwd],
},
),
];
}
if (this.lensLocation === 'all') {
const packageManager = await findPreferredPM(Uri.joinPath(document.uri, '..').fsPath);
return tokens.scripts.map(
({ name, nameRange }) =>
new CodeLens(
nameRange,
{
title,
command: 'extension.js-debug.createDebuggerTerminal',
arguments: [`${packageManager.name} run ${name}`, workspace.getWorkspaceFolder(document.uri), { cwd }],
},
),
);
}
return [];
}
/**
* @inheritdoc
*/
public dispose() {
this.subscriptions.forEach(s => s.dispose());
}
}

View file

@ -3,21 +3,20 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { JSONVisitor, visit } from 'jsonc-parser';
import * as path from 'path';
import {
commands, Event, EventEmitter, ExtensionContext,
Range,
Selection, Task,
TaskGroup, tasks, TextDocument, TextDocumentShowOptions, ThemeIcon, TreeDataProvider, TreeItem, TreeItemLabel, TreeItemCollapsibleState, Uri,
window, workspace, WorkspaceFolder
window, workspace, WorkspaceFolder, Position, Location
} from 'vscode';
import * as nls from 'vscode-nls';
import { readScripts } from './readScripts';
import {
createTask, getPackageManager, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition,
NpmTaskProvider,
startDebugging,
TaskLocation,
TaskWithLocation
} from './tasks';
@ -78,7 +77,7 @@ class NpmScript extends TreeItem {
task: Task;
package: PackageJSON;
constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task, public taskLocation?: TaskLocation) {
constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task, public taskLocation?: Location) {
super(task.name, TreeItemCollapsibleState.None);
const command: ExplorerCommands = workspace.getConfiguration('npm').get<ExplorerCommands>('scriptExplorerAction') || 'open';
@ -87,9 +86,9 @@ class NpmScript extends TreeItem {
title: 'Edit Script',
command: 'vscode.open',
arguments: [
taskLocation?.document,
taskLocation?.uri,
taskLocation ? <TextDocumentShowOptions>{
selection: new Range(taskLocation.line, taskLocation.line)
selection: new Range(taskLocation.range.start, taskLocation.range.start)
} : undefined
]
},
@ -153,37 +152,18 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
startDebugging(this.extensionContext, script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder());
}
private findScript(document: TextDocument, script?: NpmScript): number {
let scriptOffset = 0;
let inScripts = false;
private findScriptPosition(document: TextDocument, script?: NpmScript) {
const scripts = readScripts(document);
if (!scripts) {
return undefined;
}
let visitor: JSONVisitor = {
onError() {
return scriptOffset;
},
onObjectEnd() {
if (inScripts) {
inScripts = false;
}
},
onObjectProperty(property: string, offset: number, _length: number) {
if (property === 'scripts') {
inScripts = true;
if (!script) { // select the script section
scriptOffset = offset;
}
}
else if (inScripts && script) {
let label = getTaskName(property, script.task.definition.path);
if (script.task.name === label) {
scriptOffset = offset;
}
}
}
};
visit(document.getText(), visitor);
return scriptOffset;
if (!script) {
return scripts.location.range.start;
}
const found = scripts.scripts.find(s => getTaskName(s.name, script.task.definition.path) === script.task.name);
return found?.nameRange.start;
}
private async runInstall(selection: PackageJSON) {
@ -194,7 +174,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
if (!uri) {
return;
}
let task = await createTask(this.extensionContext, 'install', ['install'], selection.folder.workspaceFolder, uri, true, undefined, []);
let task = await createTask(await getPackageManager(this.context, selection.folder.workspaceFolder.uri, true), 'install', ['install'], selection.folder.workspaceFolder, uri, undefined, []);
tasks.executeTask(task);
}
@ -209,8 +189,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
return;
}
let document: TextDocument = await workspace.openTextDocument(uri);
let offset = this.findScript(document, selection instanceof NpmScript ? selection : undefined);
let position = document.positionAt(offset);
let position = this.findScriptPosition(document, selection instanceof NpmScript ? selection : undefined) || new Position(0, 0);
await window.showTextDocument(document, { preserveFocus: true, selection: new Selection(position, position) });
}

View file

@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { JSONVisitor, visit } from 'jsonc-parser';
import { Location, Position, Range, TextDocument } from 'vscode';
export interface INpmScriptReference {
name: string;
value: string;
nameRange: Range;
valueRange: Range;
}
export interface INpmScriptInfo {
location: Location;
scripts: INpmScriptReference[];
}
export const readScripts = (document: TextDocument, buffer = document.getText()): INpmScriptInfo | undefined => {
let start: Position | undefined;
let end: Position | undefined;
let inScripts = false;
let buildingScript: { name: string; nameRange: Range } | void;
let level = 0;
const scripts: INpmScriptReference[] = [];
const visitor: JSONVisitor = {
onError() {
// no-op
},
onObjectBegin() {
level++;
},
onObjectEnd(offset) {
if (inScripts) {
end = document.positionAt(offset);
inScripts = false;
}
level--;
},
onLiteralValue(value: unknown, offset: number, length: number) {
if (buildingScript && typeof value === 'string') {
scripts.push({
...buildingScript,
value,
valueRange: new Range(document.positionAt(offset), document.positionAt(offset + length)),
});
buildingScript = undefined;
}
},
onObjectProperty(property: string, offset: number, length: number) {
if (level === 1 && property === 'scripts') {
inScripts = true;
start = document.positionAt(offset);
} else if (inScripts) {
buildingScript = {
name: property,
nameRange: new Range(document.positionAt(offset), document.positionAt(offset + length))
};
}
},
};
visit(buffer, visitor);
if (start === undefined) {
return undefined;
}
return { location: new Location(document.uri, new Range(start, end ?? start)), scripts };
};

View file

@ -3,20 +3,24 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
ExtensionContext, TextDocument, commands, ProviderResult, CancellationToken,
workspace, tasks, Range, HoverProvider, Hover, Position, MarkdownString, Uri
} from 'vscode';
import {
createTask, startDebugging, findAllScriptRanges
} from './tasks';
import * as nls from 'vscode-nls';
import { dirname } from 'path';
import {
CancellationToken, commands, ExtensionContext,
Hover, HoverProvider, MarkdownString, Position, ProviderResult,
tasks, TextDocument,
Uri, workspace
} from 'vscode';
import * as nls from 'vscode-nls';
import { INpmScriptInfo, readScripts } from './readScripts';
import {
createTask,
getPackageManager, startDebugging
} from './tasks';
const localize = nls.loadMessageBundle();
let cachedDocument: Uri | undefined = undefined;
let cachedScriptsMap: Map<string, [number, number, string]> | undefined = undefined;
let cachedScripts: INpmScriptInfo | undefined = undefined;
export function invalidateHoverScriptsCache(document?: TextDocument) {
if (!document) {
@ -42,20 +46,16 @@ export class NpmScriptHoverProvider implements HoverProvider {
let hover: Hover | undefined = undefined;
if (!cachedDocument || cachedDocument.fsPath !== document.uri.fsPath) {
cachedScriptsMap = findAllScriptRanges(document.getText());
cachedScripts = readScripts(document);
cachedDocument = document.uri;
}
cachedScriptsMap!.forEach((value, key) => {
let start = document.positionAt(value[0]);
let end = document.positionAt(value[0] + value[1]);
let range = new Range(start, end);
if (range.contains(position)) {
cachedScripts?.scripts.forEach(({ name, nameRange }) => {
if (nameRange.contains(position)) {
let contents: MarkdownString = new MarkdownString();
contents.isTrusted = true;
contents.appendMarkdown(this.createRunScriptMarkdown(key, document.uri));
contents.appendMarkdown(this.createDebugScriptMarkdown(key, document.uri));
contents.appendMarkdown(this.createRunScriptMarkdown(name, document.uri));
contents.appendMarkdown(this.createDebugScriptMarkdown(name, document.uri));
hover = new Hover(contents);
}
});
@ -103,7 +103,7 @@ export class NpmScriptHoverProvider implements HoverProvider {
let documentUri = args.documentUri;
let folder = workspace.getWorkspaceFolder(documentUri);
if (folder) {
let task = await createTask(this.context, script, ['run', script], folder, documentUri);
let task = await createTask(await getPackageManager(this.context, folder.uri), script, ['run', script], folder, documentUri);
await tasks.executeTask(task);
}
}

View file

@ -5,15 +5,15 @@
import {
TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace,
DebugConfiguration, debug, TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window, Position, ExtensionContext, env,
ShellQuotedString, ShellQuoting
TaskProvider, TextDocument, tasks, TaskScope, QuickPickItem, window, Position, ExtensionContext, env,
ShellQuotedString, ShellQuoting, commands, Location
} from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as minimatch from 'minimatch';
import * as nls from 'vscode-nls';
import { JSONVisitor, visit, ParseErrorCode } from 'jsonc-parser';
import { findPreferredPM } from './preferred-pm';
import { readScripts } from './readScripts';
const localize = nls.loadMessageBundle();
@ -40,7 +40,7 @@ export interface TaskLocation {
export interface TaskWithLocation {
task: Task,
location?: TaskLocation
location?: Location
}
export class NpmTaskProvider implements TaskProvider {
@ -57,7 +57,7 @@ export class NpmTaskProvider implements TaskProvider {
return tasks.map(task => task.task);
}
public resolveTask(_task: Task): Promise<Task> | undefined {
public async resolveTask(_task: Task): Promise<Task | undefined> {
const npmTask = (<any>_task.definition).script;
if (npmTask) {
const kind: NpmTaskDefinition = (<any>_task.definition);
@ -75,7 +75,7 @@ export class NpmTaskProvider implements TaskProvider {
if (kind.script !== INSTALL_SCRIPT) {
cmd.unshift('run');
}
return createTask(this.context, kind, cmd, _task.scope, packageJsonUri);
return createTask(await getPackageManager(this.context, _task.scope.uri), kind, cmd, _task.scope, packageJsonUri);
}
return undefined;
}
@ -278,29 +278,30 @@ async function provideNpmScriptsForFolder(context: ExtensionContext, packageJson
const result: TaskWithLocation[] = [];
const prePostScripts = getPrePostScripts(scripts);
const packageManager = await getPackageManager(context, folder.uri, showWarning);
for (const each of scripts.keys()) {
const scriptValue = scripts.get(each)!;
const task = await createTask(context, each, ['run', each], folder!, packageJsonUri, showWarning, scriptValue.script);
const lowerCaseTaskName = each.toLowerCase();
for (const { name, value, nameRange } of scripts.scripts) {
const task = await createTask(packageManager, name, ['run', name], folder!, packageJsonUri, value);
const lowerCaseTaskName = name.toLowerCase();
if (isBuildTask(lowerCaseTaskName)) {
task.group = TaskGroup.Build;
} else if (isTestTask(lowerCaseTaskName)) {
task.group = TaskGroup.Test;
}
if (prePostScripts.has(each)) {
if (prePostScripts.has(name)) {
task.group = TaskGroup.Clean; // hack: use Clean group to tag pre/post scripts
}
// todo@connor4312: all scripts are now debuggable, what is a 'debug script'?
if (isDebugScript(scriptValue.script)) {
if (isDebugScript(value)) {
task.group = TaskGroup.Rebuild; // hack: use Rebuild group to tag debug scripts
}
result.push({ task, location: scriptValue.location });
result.push({ task, location: new Location(packageJsonUri, nameRange) });
}
// always add npm install (without a problem matcher)
result.push({ task: await createTask(context, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, showWarning, 'install dependencies from package', []) });
result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) });
return result;
}
@ -311,7 +312,7 @@ export function getTaskName(script: string, relativePath: string | undefined) {
return script;
}
export async function createTask(context: ExtensionContext, script: NpmTaskDefinition | string, cmd: string[], folder: WorkspaceFolder, packageJsonUri: Uri, showWarning: boolean = true, detail?: string, matcher?: any): Promise<Task> {
export async function createTask(packageManager: string, script: NpmTaskDefinition | string, cmd: string[], folder: WorkspaceFolder, packageJsonUri: Uri, detail?: string, matcher?: any): Promise<Task> {
let kind: NpmTaskDefinition;
if (typeof script === 'string') {
kind = { type: 'npm', script: script };
@ -319,7 +320,6 @@ export async function createTask(context: ExtensionContext, script: NpmTaskDefin
kind = script;
}
const packageManager = await getPackageManager(context, folder.uri, showWarning);
function getCommandLine(cmd: string[]): (string | ShellQuotedString)[] {
const result: (string | ShellQuotedString)[] = new Array(cmd.length);
for (let i = 0; i < cmd.length; i++) {
@ -392,151 +392,39 @@ export async function runScript(context: ExtensionContext, script: string, docum
let uri = document.uri;
let folder = workspace.getWorkspaceFolder(uri);
if (folder) {
let task = await createTask(context, script, ['run', script], folder, uri);
const task = await createTask(await getPackageManager(context, folder.uri), script, ['run', script], folder, uri);
tasks.executeTask(task);
}
}
export async function startDebugging(context: ExtensionContext, scriptName: string, cwd: string, folder: WorkspaceFolder) {
const config: DebugConfiguration = {
type: 'pwa-node',
request: 'launch',
name: `Debug ${scriptName}`,
cwd,
runtimeExecutable: await getPackageManager(context, folder.uri),
runtimeArgs: [
'run',
scriptName,
],
};
if (folder) {
debug.startDebugging(folder, config);
}
commands.executeCommand(
'extension.js-debug.createDebuggerTerminal',
`${await getPackageManager(context, folder.uri)} run ${scriptName}`,
folder,
{ cwd },
);
}
export type StringMap = { [s: string]: string; };
async function findAllScripts(document: TextDocument, buffer: string): Promise<Map<string, { script: string, location: TaskLocation }>> {
let scripts: Map<string, { script: string, location: TaskLocation }> = new Map();
let script: string | undefined = undefined;
let inScripts = false;
let scriptOffset = 0;
export function findScriptAtPosition(document: TextDocument, buffer: string, position: Position): string | undefined {
const read = readScripts(document, buffer);
if (!read) {
return undefined;
}
let visitor: JSONVisitor = {
onError(_error: ParseErrorCode, _offset: number, _length: number) {
console.log(_error);
},
onObjectEnd() {
if (inScripts) {
inScripts = false;
}
},
onLiteralValue(value: any, _offset: number, _length: number) {
if (script) {
if (typeof value === 'string') {
scripts.set(script, { script: value, location: { document: document.uri, line: document.positionAt(scriptOffset) } });
}
script = undefined;
}
},
onObjectProperty(property: string, offset: number, _length: number) {
if (property === 'scripts') {
inScripts = true;
}
else if (inScripts && !script) {
script = property;
scriptOffset = offset;
} else { // nested object which is invalid, ignore the script
script = undefined;
}
for (const script of read.scripts) {
if (script.nameRange.start.isBeforeOrEqual(position) && script.valueRange.end.isAfterOrEqual(position)) {
return script.name;
}
};
visit(buffer, visitor);
return scripts;
}
return undefined;
}
export function findAllScriptRanges(buffer: string): Map<string, [number, number, string]> {
let scripts: Map<string, [number, number, string]> = new Map();
let script: string | undefined = undefined;
let offset: number;
let length: number;
let inScripts = false;
let visitor: JSONVisitor = {
onError(_error: ParseErrorCode, _offset: number, _length: number) {
},
onObjectEnd() {
if (inScripts) {
inScripts = false;
}
},
onLiteralValue(value: any, _offset: number, _length: number) {
if (script) {
scripts.set(script, [offset, length, value]);
script = undefined;
}
},
onObjectProperty(property: string, off: number, len: number) {
if (property === 'scripts') {
inScripts = true;
}
else if (inScripts) {
script = property;
offset = off;
length = len;
}
}
};
visit(buffer, visitor);
return scripts;
}
export function findScriptAtPosition(buffer: string, offset: number): string | undefined {
let script: string | undefined = undefined;
let foundScript: string | undefined = undefined;
let inScripts = false;
let scriptStart: number | undefined;
let visitor: JSONVisitor = {
onError(_error: ParseErrorCode, _offset: number, _length: number) {
},
onObjectEnd() {
if (inScripts) {
inScripts = false;
scriptStart = undefined;
}
},
onLiteralValue(value: any, nodeOffset: number, nodeLength: number) {
if (inScripts && scriptStart) {
if (typeof value === 'string' && offset >= scriptStart && offset < nodeOffset + nodeLength) {
// found the script
inScripts = false;
foundScript = script;
} else {
script = undefined;
}
}
},
onObjectProperty(property: string, nodeOffset: number) {
if (property === 'scripts') {
inScripts = true;
}
else if (inScripts) {
scriptStart = nodeOffset;
script = property;
} else { // nested object which is invalid, ignore the script
script = undefined;
}
}
};
visit(buffer, visitor);
return foundScript;
}
export async function getScripts(packageJsonUri: Uri): Promise<Map<string, { script: string, location: TaskLocation }> | undefined> {
export async function getScripts(packageJsonUri: Uri) {
if (packageJsonUri.scheme !== 'file') {
return undefined;
}
@ -548,9 +436,7 @@ export async function getScripts(packageJsonUri: Uri): Promise<Map<string, { scr
try {
const document: TextDocument = await workspace.openTextDocument(packageJsonUri);
let contents = document.getText();
let json = findAllScripts(document, contents);//JSON.parse(contents);
return json;
return readScripts(document);
} catch (e) {
let localizedParseError = localize('npm.parseError', 'Npm task detection: failed to parse the file {0}', packageJsonUri.fsPath);
throw new Error(localizedParseError);

View file

@ -7,7 +7,7 @@
"tooltip.run": "Run {0}",
"tooltip.debug": "Debug {0}",
"tooltip.runState": "{0}/{0} Tests Passed",
"tooltip.runState": "{0}/{1} Tests Passed",
"tooltip.runStateWithDuration": "{0}/{1} Tests Passed in {2}",
"state.failed": "Failed",

View file

@ -17,7 +17,6 @@
],
"dependencies": {
"jsonc-parser": "^2.2.1",
"rimraf": "^2.6.3",
"semver": "5.5.1",
"typescript-vscode-sh-plugin": "^0.6.14",
"vscode-extension-telemetry": "0.1.1",

View file

@ -3,9 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as rimraf from 'rimraf';
import * as fs from 'fs';
import * as vscode from 'vscode';
import { Api, getExtensionApi } from './api';
import { CommandManager } from './commands/commandManager';
import { registerBaseCommands } from './commands/index';
import { LanguageConfigurationManager } from './languageFeatures/languageConfiguration';
import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost';
@ -13,7 +14,6 @@ import { nodeRequestCancellerFactory } from './tsServer/cancellation.electron';
import { NodeLogDirectoryProvider } from './tsServer/logDirectoryProvider.electron';
import { ChildServerProcess } from './tsServer/serverProcess.electron';
import { DiskTypeScriptVersionProvider } from './tsServer/versionProvider.electron';
import { CommandManager } from './commands/commandManager';
import { onCaseInsenitiveFileSystem } from './utils/fileSystem.electron';
import { PluginManager } from './utils/plugins';
import * as temp from './utils/temp.electron';
@ -62,5 +62,5 @@ export function activate(
}
export function deactivate() {
rimraf.sync(temp.getInstanceTempDir());
fs.rmdirSync(temp.getInstanceTempDir(), { recursive: true });
}

View file

@ -47,24 +47,6 @@ applicationinsights@1.0.8:
diagnostic-channel-publishers "0.2.1"
zone.js "0.7.6"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
diagnostic-channel-publishers@0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3"
@ -77,67 +59,11 @@ diagnostic-channel@0.2.0:
dependencies:
semver "^5.3.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
jsonc-parser@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342"
integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
semver@5.5.1:
version "5.5.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
@ -165,11 +91,6 @@ vscode-nls@^4.1.1:
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
zone.js@0.7.6:
version "0.7.6"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009"

View file

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'mocha';
import * as assert from 'assert';
import * as vscode from 'vscode';
suite('vscode server cli', () => {
test('extension is installed and enabled when installed by server cli', function () {
const extension = process.env.TESTRESOLVER_INSTALL_BUILTIN_EXTENSION;
if (!process.env.BUILD_SOURCEVERSION // Skip it when running out of sources
|| !process.env.REMOTE_VSCODE // Skip it when not a remote integration test
|| !extension // Skip it when extension is not provided to server
) {
this.skip();
}
assert.ok(vscode.extensions.getExtension(extension!));
});
});

View file

@ -83,12 +83,6 @@ export function activate(context: vscode.ExtensionContext) {
const env = getNewEnv();
const remoteDataDir = process.env['TESTRESOLVER_DATA_FOLDER'] || path.join(os.homedir(), serverDataFolderName || `${dataFolderName}-testresolver`);
const remoteExtension = process.env['TESTRESOLVER_REMOTE_EXTENSION'];
if (remoteExtension) {
commandArgs.push('--install-extension', remoteExtension);
commandArgs.push('--start-server');
}
env['VSCODE_AGENT_FOLDER'] = remoteDataDir;
outputChannel.appendLine(`Using data folder at ${remoteDataDir}`);
@ -98,6 +92,11 @@ export function activate(context: vscode.ExtensionContext) {
const serverCommandPath = path.join(vscodePath, 'resources', 'server', 'bin-dev', serverCommand);
extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath });
} else {
const extensionToInstall = process.env['TESTRESOLVER_INSTALL_BUILTIN_EXTENSION'];
if (extensionToInstall) {
commandArgs.push('--install-builtin-extension', extensionToInstall);
commandArgs.push('--start-server');
}
const serverCommand = process.platform === 'win32' ? 'server.cmd' : 'server.sh';
let serverLocation = env['VSCODE_REMOTE_SERVER_PATH']; // support environment variable to specify location of server on disk
if (!serverLocation) {

View file

@ -1,7 +1,7 @@
{
"name": "code-oss-dev",
"version": "1.54.0",
"distro": "a1836302c2aaab6ded8ad4a0871f74dbc42359f5",
"distro": "e6efd9b7780f53a499ee7c8f1728ffbc3391d8fb",
"author": {
"name": "Microsoft Corporation"
},
@ -222,4 +222,4 @@
"elliptic": "^6.5.3",
"nwmatcher": "^1.4.4"
}
}
}

View file

@ -91,7 +91,7 @@
},
{
"name": "ms-vscode.js-debug",
"version": "1.53.0",
"version": "1.54.0",
"repo": "https://github.com/microsoft/vscode-js-debug",
"metadata": {
"id": "25629058-ddac-4e17-abba-74678e126c5d",

View file

@ -119,4 +119,5 @@ export const isWebkitWebView = (!isChrome && !isSafari && isWebKit);
export const isIPad = (userAgent.indexOf('iPad') >= 0 || (isSafari && navigator.maxTouchPoints > 0));
export const isEdgeLegacyWebView = isEdgeLegacy && (userAgent.indexOf('WebView/') >= 0);
export const isElectron = (userAgent.indexOf('Electron/') >= 0);
export const isAndroid = (userAgent.indexOf('Android') >= 0);
export const isStandalone = (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches);

View file

@ -746,7 +746,7 @@ export class MenuBar extends Disposable {
private setUnfocusedState(): void {
if (this.options.visibility === 'toggle' || this.options.visibility === 'hidden') {
this.focusState = MenubarState.HIDDEN;
} else if (this.options.visibility === 'default' && browser.isFullscreen()) {
} else if (this.options.visibility === 'classic' && browser.isFullscreen()) {
this.focusState = MenubarState.HIDDEN;
} else {
this.focusState = MenubarState.VISIBLE;
@ -838,6 +838,22 @@ export class MenuBar extends Disposable {
this._mnemonicsInUse = value;
}
private get shouldAltKeyFocus(): boolean {
if (isMacintosh) {
return false;
}
if (!this.options.disableAltFocus) {
return true;
}
if (this.options.visibility === 'toggle') {
return true;
}
return false;
}
public get onVisibilityChange(): Event<boolean> {
return this._onVisibilityChange.event;
}
@ -869,7 +885,7 @@ export class MenuBar extends Disposable {
}
// Prevent alt-key default if the menu is not hidden and we use alt to focus
if (modifierKeyStatus.event && !this.options.disableAltFocus) {
if (modifierKeyStatus.event && this.shouldAltKeyFocus) {
if (ScanCodeUtils.toEnum(modifierKeyStatus.event.code) === ScanCode.AltLeft) {
modifierKeyStatus.event.preventDefault();
}
@ -885,7 +901,7 @@ export class MenuBar extends Disposable {
// Clean alt key press and release
if (allModifiersReleased && modifierKeyStatus.lastKeyPressed === 'alt' && modifierKeyStatus.lastKeyReleased === 'alt') {
if (!this.awaitingAltRelease) {
if (!this.isFocused && !(this.options.disableAltFocus && this.options.visibility !== 'toggle')) {
if (!this.isFocused && this.shouldAltKeyFocus) {
this.mnemonicsInUse = true;
this.focusedMenu = { index: this.numMenusShown > 0 ? 0 : MenuBar.OVERFLOW_INDEX };
this.focusState = MenubarState.FOCUSED;

View file

@ -542,6 +542,7 @@ export namespace Codicon {
export const typeHierarchy = new Codicon('type-hierarchy', { character: '\\ebb9' });
export const typeHierarchySub = new Codicon('type-hierarchy-sub', { character: '\\ebba' });
export const typeHierarchySuper = new Codicon('type-hierarchy-super', { character: '\\ebbb' });
export const gitPullRequestCreate = new Codicon('git-pull-request-create', { character: '\\ebbc' });
export const dropDownButton = new Codicon('drop-down-button', Codicon.chevronDown.definition);
}

View file

@ -131,21 +131,30 @@ async function safeReaddirWithFileTypes(path: string): Promise<IDirent[]> {
// previously.
// This can only really happen on exotic file systems
// such as explained in #115645 where we get entries
// from `readdir` that we can later not `lstat`.
// from `readdir` that we can later not `lstat`.
const result: IDirent[] = [];
const children = await readdir(path);
for (const child of children) {
let isFile = false;
let isDirectory = false;
let isSymbolicLink = false;
try {
const lstat = await fs.promises.lstat(join(path, child));
result.push({
name: child,
isFile: () => lstat.isFile(),
isDirectory: () => lstat.isDirectory(),
isSymbolicLink: () => lstat.isSymbolicLink()
});
isFile = lstat.isFile();
isDirectory = lstat.isDirectory();
isSymbolicLink = lstat.isSymbolicLink();
} catch (error) {
console.warn('[node.js fs] unexpected error from lstat after readdir: ', error);
}
result.push({
name: child,
isFile: () => isFile,
isDirectory: () => isDirectory,
isSymbolicLink: () => isSymbolicLink
});
}
return result;

View file

@ -5,10 +5,14 @@
(function () {
let MonacoEnvironment = (<any>self).MonacoEnvironment;
let monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../';
const MonacoEnvironment = (<any>self).MonacoEnvironment;
const monacoBaseUrl = MonacoEnvironment && MonacoEnvironment.baseUrl ? MonacoEnvironment.baseUrl : '../../../';
const trustedTypesPolicy = self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value });
const trustedTypesPolicy = (
typeof self.trustedTypes?.createPolicy === 'function'
? self.trustedTypes?.createPolicy('amdLoader', { createScriptURL: value => value })
: undefined
);
if (typeof (<any>self).define !== 'function' || !(<any>self).define.amd) {
let loaderSrc: string | TrustedScriptURL = monacoBaseUrl + 'vs/loader.js';

View file

@ -29,7 +29,7 @@ import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetry
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { ILogService, ILoggerService, MultiplexLogService, ConsoleLogService } from 'vs/platform/log/common/log';
import { ILogService, ILoggerService, MultiplexLogService, ConsoleLogger } from 'vs/platform/log/common/log';
import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc';
import { LocalizationsService } from 'vs/platform/localizations/node/localizations';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
@ -41,7 +41,7 @@ import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedPr
import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner';
import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner';
import { IMainProcessService, MessagePortMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { DiagnosticsService, IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
@ -144,8 +144,8 @@ class SharedProcessMain extends Disposable {
const mainRouter = new StaticRouter(ctx => ctx === 'main');
const loggerClient = new LoggerChannelClient(this.server.getChannel('logger', mainRouter)); // we only use this for log levels
const multiplexLogger = this._register(new MultiplexLogService([
this._register(new ConsoleLogService(this.configuration.logLevel)),
this._register(new SpdLogService('sharedprocess', environmentService.logsPath, this.configuration.logLevel))
this._register(new ConsoleLogger(this.configuration.logLevel)),
this._register(new SpdLogLogger('sharedprocess', environmentService.logsPath, this.configuration.logLevel))
]));
const logService = this._register(new FollowerLogService(loggerClient, multiplexLogger));

View file

@ -20,7 +20,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log';
import { ILogService, ConsoleMainLogger, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log';
import { StateService } from 'vs/platform/state/node/stateService';
import { IStateService } from 'vs/platform/state/node/state';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
@ -31,7 +31,7 @@ import { IRequestService } from 'vs/platform/request/common/request';
import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService';
import { CodeApplication } from 'vs/code/electron-main/app';
import { getPathLabel, mnemonicButtonLabel } from 'vs/base/common/labels';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { setUnexpectedErrorHandler } from 'vs/base/common/errors';
import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
@ -133,7 +133,7 @@ class CodeMain {
const mainIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, true);
bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel());
bufferLogService.logger = new SpdLogLogger('main', environmentService.logsPath, bufferLogService.getLevel());
once(lifecycleMainService.onWillShutdown)(() => {
fileService.dispose();
(configurationService as ConfigurationService).dispose();
@ -154,7 +154,7 @@ class CodeMain {
services.set(IEnvironmentService, environmentService);
services.set(IEnvironmentMainService, environmentService);
const logService = new MultiplexLogService([new ConsoleLogMainService(getLogLevel(environmentService)), bufferLogService]);
const logService = new MultiplexLogService([new ConsoleMainLogger(getLogLevel(environmentService)), bufferLogService]);
process.once('exit', () => logService.dispose());
services.set(ILogService, logService);

View file

@ -1192,7 +1192,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
private getMenuBarVisibility(): MenuBarVisibility {
let menuBarVisibility = getMenuBarVisibility(this.configurationService);
if (['visible', 'toggle', 'hidden'].indexOf(menuBarVisibility) < 0) {
menuBarVisibility = 'default';
menuBarVisibility = 'classic';
}
return menuBarVisibility;
@ -1227,7 +1227,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
const isFullscreen = this.isFullScreen;
switch (visibility) {
case ('default'):
case ('classic'):
this._win.setMenuBarVisibility(!isFullscreen);
this._win.autoHideMenuBar = isFullscreen;
break;

View file

@ -30,9 +30,9 @@ import { ConfigurationService } from 'vs/platform/configuration/common/configura
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
import { IStateService } from 'vs/platform/state/node/state';
import { StateService } from 'vs/platform/state/node/stateService';
import { ILogService, getLogLevel, LogLevel, ConsoleLogService, MultiplexLogService } from 'vs/platform/log/common/log';
import { ILogService, getLogLevel, LogLevel, ConsoleLogger, MultiplexLogService, ILogger } from 'vs/platform/log/common/log';
import { Schemas } from 'vs/base/common/network';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { buildTelemetryMessage } from 'vs/platform/telemetry/node/telemetry';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
@ -104,10 +104,10 @@ class CliMain extends Disposable {
// Log
const logLevel = getLogLevel(environmentService);
const loggers: ILogService[] = [];
loggers.push(new SpdLogService('cli', environmentService.logsPath, logLevel));
const loggers: ILogger[] = [];
loggers.push(new SpdLogLogger('cli', environmentService.logsPath, logLevel));
if (logLevel === LogLevel.Trace) {
loggers.push(new ConsoleLogService(logLevel));
loggers.push(new ConsoleLogger(logLevel));
}
const logService = this._register(new MultiplexLogService(loggers));

View file

@ -1931,6 +1931,7 @@ registerOverwritableCommand(Handler.Type, {
}]
});
registerOverwritableCommand(Handler.ReplacePreviousChar);
registerOverwritableCommand(Handler.CompositionType);
registerOverwritableCommand(Handler.CompositionStart);
registerOverwritableCommand(Handler.CompositionEnd);
registerOverwritableCommand(Handler.Paste);

View file

@ -42,6 +42,7 @@ export interface IPointerHandlerHelper {
linesContentDomNode: HTMLElement;
focusTextArea(): void;
dispatchTextAreaEvent(event: CustomEvent): void;
/**
* Get the last rendered information for cursors & textarea.

View file

@ -13,6 +13,7 @@ import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/e
import { ViewController } from 'vs/editor/browser/view/viewController';
import { ViewContext } from 'vs/editor/common/view/viewContext';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
import { TextAreaSyntethicEvents } from 'vs/editor/browser/controller/textAreaInput';
interface IThrottledGestureEvent {
translationX: number;
@ -210,6 +211,11 @@ class TouchHandler extends MouseHandler {
const target = this._createMouseTarget(new EditorMouseEvent(event, this.viewHelper.viewDomNode), false);
if (target.position) {
// Send the tap event also to the <textarea> (for input purposes)
const event = document.createEvent('CustomEvent');
event.initEvent(TextAreaSyntethicEvents.Tap, false, true);
this.viewHelper.dispatchTextAreaEvent(event);
this.viewController.moveTo(target.position);
}
}

View file

@ -12,7 +12,7 @@ import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { Configuration } from 'vs/editor/browser/config/configuration';
import { CopyOptions, ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, ClipboardDataToCopy } from 'vs/editor/browser/controller/textAreaInput';
import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState } from 'vs/editor/browser/controller/textAreaState';
import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState';
import { ViewController } from 'vs/editor/browser/view/viewController';
import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart';
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
@ -202,6 +202,22 @@ export class TextAreaHandler extends ViewPart {
return TextAreaState.EMPTY;
}
if (browser.isAndroid) {
// when tapping in the editor on a word, Android enters composition mode.
// in the `compositionstart` event we cannot clear the textarea, because
// it then forgets to ever send a `compositionend`.
// we therefore only write the current word in the textarea
const selection = this._selections[0];
if (selection.isEmpty()) {
const position = selection.getStartPosition();
const [wordAtPosition, positionOffsetInWord] = this._getAndroidWordAtPosition(position);
if (wordAtPosition.length > 0) {
return new TextAreaState(wordAtPosition, positionOffsetInWord, positionOffsetInWord, position, position);
}
}
return TextAreaState.EMPTY;
}
return PagedScreenReaderStrategy.fromEditorSelection(currentState, simpleModel, this._selections[0], this._accessibilityPageSize, this._accessibilitySupport === AccessibilitySupport.Unknown);
},
@ -237,9 +253,16 @@ export class TextAreaHandler extends ViewPart {
}));
this._register(this._textAreaInput.onType((e: ITypeData) => {
if (e.replaceCharCnt) {
this._viewController.replacePreviousChar(e.text, e.replaceCharCnt);
if (e.replacePrevCharCnt || e.replaceNextCharCnt || e.positionDelta) {
// must be handled through the new command
if (_debugComposition) {
console.log(` => compositionType: <<${e.text}>>, ${e.replacePrevCharCnt}, ${e.replaceNextCharCnt}, ${e.positionDelta}`);
}
this._viewController.compositionType(e.text, e.replacePrevCharCnt, e.replaceNextCharCnt, e.positionDelta);
} else {
if (_debugComposition) {
console.log(` => type: <<${e.text}>>`);
}
this._viewController.type(e.text);
}
}));
@ -250,7 +273,7 @@ export class TextAreaHandler extends ViewPart {
this._register(this._textAreaInput.onCompositionStart((e) => {
const lineNumber = this._selections[0].startLineNumber;
const column = this._selections[0].startColumn - (e.moveOneCharacterLeft ? 1 : 0);
const column = this._selections[0].startColumn + e.revealDeltaColumns;
this._context.model.revealRange(
'keyboard',
@ -280,8 +303,11 @@ export class TextAreaHandler extends ViewPart {
}));
this._register(this._textAreaInput.onCompositionUpdate((e: ICompositionData) => {
if (!this._visibleTextArea) {
return;
}
// adjust width by its size
this._visibleTextArea = this._visibleTextArea!.setWidth(measureText(e.data, this._fontInfo));
this._visibleTextArea = this._visibleTextArea.setWidth(measureText(e.data, this._fontInfo));
this._render();
}));
@ -308,6 +334,47 @@ export class TextAreaHandler extends ViewPart {
super.dispose();
}
private _getAndroidWordAtPosition(position: Position): [string, number] {
const ANDROID_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:",.<>/?';
const lineContent = this._context.model.getLineContent(position.lineNumber);
const wordSeparators = getMapForWordSeparators(ANDROID_WORD_SEPARATORS);
let goingLeft = true;
let startColumn = position.column;
let goingRight = true;
let endColumn = position.column;
let distance = 0;
while (distance < 50 && (goingLeft || goingRight)) {
if (goingLeft && startColumn <= 1) {
goingLeft = false;
}
if (goingLeft) {
const charCode = lineContent.charCodeAt(startColumn - 2);
const charClass = wordSeparators.get(charCode);
if (charClass !== WordCharacterClass.Regular) {
goingLeft = false;
} else {
startColumn--;
}
}
if (goingRight && endColumn > lineContent.length) {
goingRight = false;
}
if (goingRight) {
const charCode = lineContent.charCodeAt(endColumn - 1);
const charClass = wordSeparators.get(charCode);
if (charClass !== WordCharacterClass.Regular) {
goingRight = false;
} else {
endColumn++;
}
}
distance++;
}
return [lineContent.substring(startColumn - 1, endColumn - 1), position.column - startColumn];
}
private _getWordBeforePosition(position: Position): string {
const lineContent = this._context.model.getLineContent(position.lineNumber);
const wordSeparators = getMapForWordSeparators(this._context.configuration.options.get(EditorOption.wordSeparators));

View file

@ -18,6 +18,10 @@ import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { BrowserFeatures } from 'vs/base/browser/canIUse';
export namespace TextAreaSyntethicEvents {
export const Tap = '-monaco-textarea-synthetic-tap';
}
export interface ICompositionData {
data: string;
}
@ -96,7 +100,7 @@ export class InMemoryClipboardMetadataManager {
}
export interface ICompositionStartEvent {
moveOneCharacterLeft: boolean;
revealDeltaColumns: number;
}
/**
@ -204,7 +208,6 @@ export class TextAreaInput extends Disposable {
}
this._isDoingComposition = true;
let moveOneCharacterLeft = false;
if (
platform.isMacintosh
&& lastKeyDown
@ -212,17 +215,12 @@ export class TextAreaInput extends Disposable {
&& this._textAreaState.selectionStart === this._textAreaState.selectionEnd
&& this._textAreaState.selectionStart > 0
&& this._textAreaState.value.substr(this._textAreaState.selectionStart - 1, 1) === e.data
&& (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft')
) {
// Handling long press case on macOS + arrow key => pretend the character was selected
if (lastKeyDown.code === 'ArrowRight' || lastKeyDown.code === 'ArrowLeft') {
if (_debugComposition) {
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
}
moveOneCharacterLeft = true;
if (_debugComposition) {
console.log(`[compositionstart] Handling long press case on macOS + arrow key`, e);
}
}
if (moveOneCharacterLeft) {
this._textAreaState = new TextAreaState(
this._textAreaState.value,
this._textAreaState.selectionStart - 1,
@ -230,11 +228,19 @@ export class TextAreaInput extends Disposable {
this._textAreaState.selectionStartPosition ? new Position(this._textAreaState.selectionStartPosition.lineNumber, this._textAreaState.selectionStartPosition.column - 1) : null,
this._textAreaState.selectionEndPosition
);
} else {
this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY);
this._onCompositionStart.fire({ revealDeltaColumns: -1 });
return;
}
this._onCompositionStart.fire({ moveOneCharacterLeft });
if (browser.isAndroid) {
// when tapping on the editor, Android enters composition mode to edit the current word
// so we cannot clear the textarea on Android and we must pretend the current word was selected
this._onCompositionStart.fire({ revealDeltaColumns: -this._textAreaState.selectionStart });
return;
}
this._setAndWriteTextAreaState('compositionstart', TextAreaState.EMPTY);
this._onCompositionStart.fire({ revealDeltaColumns: 0 });
}));
/**
@ -246,6 +252,12 @@ export class TextAreaInput extends Disposable {
return [newState, TextAreaState.deduceInput(oldState, newState, couldBeEmojiInput)];
};
const deduceAndroidCompositionInput = (): [TextAreaState, ITypeData] => {
const oldState = this._textAreaState;
const newState = TextAreaState.readFromTextArea(this._textArea);
return [newState, TextAreaState.deduceAndroidCompositionInput(oldState, newState)];
};
/**
* Deduce the composition input from a string.
*/
@ -254,7 +266,9 @@ export class TextAreaInput extends Disposable {
const newState = TextAreaState.selectedText(text);
const typeInput: ITypeData = {
text: newState.value,
replaceCharCnt: oldState.selectionEnd - oldState.selectionStart
replacePrevCharCnt: oldState.selectionEnd - oldState.selectionStart,
replaceNextCharCnt: 0,
positionDelta: 0
};
return [newState, typeInput];
};
@ -263,6 +277,17 @@ export class TextAreaInput extends Disposable {
if (_debugComposition) {
console.log(`[compositionupdate]`, e);
}
if (browser.isAndroid) {
// On Android, the data sent with the composition update event is unusable.
// For example, if the cursor is in the middle of a word like Mic|osoft
// and Microsoft is chosen from the keyboard's suggestions, the e.data will contain "Microsoft".
// This is not really usable because it doesn't tell us where the edit began and where it ended.
const [newState, typeInput] = deduceAndroidCompositionInput();
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionUpdate.fire(e);
return;
}
const [newState, typeInput] = deduceComposition(e.data || '');
this._textAreaState = newState;
this._onType.fire(typeInput);
@ -278,6 +303,19 @@ export class TextAreaInput extends Disposable {
if (!this._isDoingComposition) {
return;
}
this._isDoingComposition = false;
if (browser.isAndroid) {
// On Android, the data sent with the composition update event is unusable.
// For example, if the cursor is in the middle of a word like Mic|osoft
// and Microsoft is chosen from the keyboard's suggestions, the e.data will contain "Microsoft".
// This is not really usable because it doesn't tell us where the edit began and where it ended.
const [newState, typeInput] = deduceAndroidCompositionInput();
this._textAreaState = newState;
this._onType.fire(typeInput);
this._onCompositionEnd.fire();
return;
}
const [newState, typeInput] = deduceComposition(e.data || '');
this._textAreaState = newState;
@ -290,11 +328,6 @@ export class TextAreaInput extends Disposable {
this._textAreaState = TextAreaState.readFromTextArea(this._textArea);
}
if (!this._isDoingComposition) {
return;
}
this._isDoingComposition = false;
this._onCompositionEnd.fire();
}));
@ -308,18 +341,18 @@ export class TextAreaInput extends Disposable {
}
const [newState, typeInput] = deduceInputFromTextAreaValue(/*couldBeEmojiInput*/platform.isMacintosh);
if (typeInput.replaceCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
if (typeInput.replacePrevCharCnt === 0 && typeInput.text.length === 1 && strings.isHighSurrogate(typeInput.text.charCodeAt(0))) {
// Ignore invalid input but keep it around for next time
return;
}
this._textAreaState = newState;
if (this._nextCommand === ReadFromTextArea.Type) {
if (typeInput.text !== '') {
if (typeInput.text !== '' || typeInput.replacePrevCharCnt !== 0) {
this._onType.fire(typeInput);
}
} else {
if (typeInput.text !== '' || typeInput.replaceCharCnt !== 0) {
if (typeInput.text !== '' || typeInput.replacePrevCharCnt !== 0) {
this._firePaste(typeInput.text, null);
}
this._nextCommand = ReadFromTextArea.Type;
@ -388,6 +421,21 @@ export class TextAreaInput extends Disposable {
}
this._setHasFocus(false);
}));
this._register(dom.addDisposableListener(textArea.domNode, TextAreaSyntethicEvents.Tap, () => {
if (browser.isAndroid && this._isDoingComposition) {
// on Android, tapping does not cancel the current composition, so the
// textarea is stuck showing the old composition
// Clear the flag to be able to write to the textarea
this._isDoingComposition = false;
// Clear the textarea to avoid an unwanted cursor type
this.writeScreenReaderContent('tapWithoutCompositionEnd');
// Fire artificial composition end
this._onCompositionEnd.fire();
}
}));
}
private _installSelectionChangeListener(): IDisposable {

View file

@ -27,7 +27,9 @@ export interface ISimpleModel {
export interface ITypeData {
text: string;
replaceCharCnt: number;
replacePrevCharCnt: number;
replaceNextCharCnt: number;
positionDelta: number;
}
export class TextAreaState {
@ -105,7 +107,9 @@ export class TextAreaState {
// This is the EMPTY state
return {
text: '',
replaceCharCnt: 0
replacePrevCharCnt: 0,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
@ -178,7 +182,9 @@ export class TextAreaState {
if (/\uFE0F/.test(potentialEmojiInput) || strings.containsEmoji(potentialEmojiInput)) {
return {
text: potentialEmojiInput,
replaceCharCnt: 0
replacePrevCharCnt: 0,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
}
@ -197,7 +203,9 @@ export class TextAreaState {
if (strings.containsFullWidthCharacter(currentValue)) {
return {
text: '',
replaceCharCnt: 0
replacePrevCharCnt: 0,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
}
@ -210,7 +218,9 @@ export class TextAreaState {
return {
text: currentValue,
replaceCharCnt: replacePreviousCharacters
replacePrevCharCnt: replacePreviousCharacters,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
@ -218,7 +228,57 @@ export class TextAreaState {
const replacePreviousCharacters = previousSelectionEnd - previousSelectionStart;
return {
text: currentValue,
replaceCharCnt: replacePreviousCharacters
replacePrevCharCnt: replacePreviousCharacters,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
public static deduceAndroidCompositionInput(previousState: TextAreaState, currentState: TextAreaState): ITypeData {
if (!previousState) {
// This is the EMPTY state
return {
text: '',
replacePrevCharCnt: 0,
replaceNextCharCnt: 0,
positionDelta: 0
};
}
if (_debugComposition) {
console.log('------------------------deduceAndroidCompositionInput');
console.log('PREVIOUS STATE: ' + previousState.toString());
console.log('CURRENT STATE: ' + currentState.toString());
}
if (previousState.value === currentState.value) {
return {
text: '',
replacePrevCharCnt: 0,
replaceNextCharCnt: 0,
positionDelta: currentState.selectionEnd - previousState.selectionEnd
};
}
const prefixLength = Math.min(strings.commonPrefixLength(previousState.value, currentState.value), previousState.selectionEnd);
const suffixLength = Math.min(strings.commonSuffixLength(previousState.value, currentState.value), previousState.value.length - previousState.selectionEnd);
const previousValue = previousState.value.substring(prefixLength, previousState.value.length - suffixLength);
const currentValue = currentState.value.substring(prefixLength, currentState.value.length - suffixLength);
const previousSelectionStart = previousState.selectionStart - prefixLength;
const previousSelectionEnd = previousState.selectionEnd - prefixLength;
const currentSelectionStart = currentState.selectionStart - prefixLength;
const currentSelectionEnd = currentState.selectionEnd - prefixLength;
if (_debugComposition) {
console.log('AFTER DIFFING PREVIOUS STATE: <' + previousValue + '>, selectionStart: ' + previousSelectionStart + ', selectionEnd: ' + previousSelectionEnd);
console.log('AFTER DIFFING CURRENT STATE: <' + currentValue + '>, selectionStart: ' + currentSelectionStart + ', selectionEnd: ' + currentSelectionEnd);
}
return {
text: currentValue,
replacePrevCharCnt: previousSelectionEnd,
replaceNextCharCnt: previousValue.length - previousSelectionEnd,
positionDelta: currentSelectionEnd - currentValue.length
};
}
}

View file

@ -15,6 +15,7 @@ import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
import { ILogService } from 'vs/platform/log/common/log';
import { IExternalOpener, IExternalUriResolver, IOpener, IOpenerService, IResolvedExternalUri, IValidator, matchesScheme, OpenOptions, ResolveExternalUriOptions } from 'vs/platform/opener/common/opener';
class CommandOpener implements IOpener {
@ -106,6 +107,7 @@ export class OpenerService implements IOpenerService {
constructor(
@ICodeEditorService editorService: ICodeEditorService,
@ICommandService commandService: ICommandService,
@ILogService private logService: ILogService
) {
// Default external opener is going through window.open()
this._defaultExternalOpener = {
@ -166,7 +168,8 @@ export class OpenerService implements IOpenerService {
// check with contributed validators
const targetURI = typeof target === 'string' ? URI.parse(target) : target;
// validate against the original URI that this URI resolves to, if one exists
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target;
const validationTarget = this._resolvedUriTargets.get(targetURI) ?? targetURI;
this.logService.trace(`OpenerService#open: ${targetURI.authority} validating via ${validationTarget.authority}`);
for (const validator of this._validators) {
if (!(await validator.shouldOpen(validationTarget))) {
return false;
@ -188,7 +191,10 @@ export class OpenerService implements IOpenerService {
for (const resolver of this._resolvers) {
const result = await resolver.resolveExternalUri(resource, options);
if (result) {
this._resolvedUriTargets.set(result.resolved, resource);
if (!this._resolvedUriTargets.has(result.resolved)) {
this.logService.trace(`OpenerService#resolveExternalUri: ${resource.authority} resolved to ${result.resolved.authority}`);
this._resolvedUriTargets.set(result.resolved, resource);
}
return result;
}
}

View file

@ -37,7 +37,7 @@ export interface IMouseDispatchData {
export interface ICommandDelegate {
paste(text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void;
type(text: string): void;
replacePreviousChar(text: string, replaceCharCnt: number): void;
compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void;
startComposition(): void;
endComposition(): void;
cut(): void;
@ -70,8 +70,8 @@ export class ViewController {
this.commandDelegate.type(text);
}
public replacePreviousChar(text: string, replaceCharCnt: number): void {
this.commandDelegate.replacePreviousChar(text, replaceCharCnt);
public compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void {
this.commandDelegate.compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta);
}
public compositionStart(): void {

View file

@ -239,6 +239,10 @@ export class View extends ViewEventHandler {
this.focus();
},
dispatchTextAreaEvent: (event: CustomEvent) => {
this._textAreaHandler.textArea.domNode.dispatchEvent(event);
},
getLastRenderData: (): PointerHandlerLastRenderData => {
const lastViewCursorsRenderData = this._viewCursors.getLastRenderData() || [];
const lastTextareaPosition = this._textAreaHandler.getLastRenderData();

View file

@ -1002,7 +1002,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
}
case editorCommon.Handler.ReplacePreviousChar: {
const args = <Partial<editorCommon.ReplacePreviousCharPayload>>payload;
this._replacePreviousChar(source, args.text || '', args.replaceCharCnt || 0);
this._compositionType(source, args.text || '', args.replaceCharCnt || 0, 0, 0);
return;
}
case editorCommon.Handler.CompositionType: {
const args = <Partial<editorCommon.CompositionTypePayload>>payload;
this._compositionType(source, args.text || '', args.replacePrevCharCnt || 0, args.replaceNextCharCnt || 0, args.positionDelta || 0);
return;
}
case editorCommon.Handler.Paste: {
@ -1061,11 +1066,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
}
}
private _replacePreviousChar(source: string | null | undefined, text: string, replaceCharCnt: number): void {
private _compositionType(source: string | null | undefined, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): void {
if (!this._modelData) {
return;
}
this._modelData.viewModel.replacePreviousChar(text, replaceCharCnt, source);
this._modelData.viewModel.compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source);
}
private _paste(source: string | null | undefined, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void {
@ -1583,8 +1588,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
type: (text: string) => {
this._type('keyboard', text);
},
replacePreviousChar: (text: string, replaceCharCnt: number) => {
this._replacePreviousChar('keyboard', text, replaceCharCnt);
compositionType: (text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number) => {
this._compositionType('keyboard', text, replacePrevCharCnt, replaceNextCharCnt, positionDelta);
},
startComposition: () => {
this._startComposition();
@ -1606,9 +1611,16 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE
const payload: editorCommon.TypePayload = { text };
this._commandService.executeCommand(editorCommon.Handler.Type, payload);
},
replacePreviousChar: (text: string, replaceCharCnt: number) => {
const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt };
this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload);
compositionType: (text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number) => {
// Try if possible to go through the existing `replacePreviousChar` command
if (replaceNextCharCnt || positionDelta) {
// must be handled through the new command
const payload: editorCommon.CompositionTypePayload = { text, replacePrevCharCnt, replaceNextCharCnt, positionDelta };
this._commandService.executeCommand(editorCommon.Handler.CompositionType, payload);
} else {
const payload: editorCommon.ReplacePreviousCharPayload = { text, replaceCharCnt: replacePrevCharCnt };
this._commandService.executeCommand(editorCommon.Handler.ReplacePreviousChar, payload);
}
},
startComposition: () => {
this._commandService.executeCommand(editorCommon.Handler.CompositionStart, {});

View file

@ -509,7 +509,7 @@ const editorConfiguration: IConfigurationNode = {
nls.localize('wordBasedSuggestionsMode.matchingDocuments', 'Suggest words from all open documents of the same language.'),
nls.localize('wordBasedSuggestionsMode.allDocuments', 'Suggest words from all open documents.')
],
description: nls.localize('wordBasedSuggestionsMode', "Controls form what documents word based completions are computed.")
description: nls.localize('wordBasedSuggestionsMode', "Controls from what documents word based completions are computed.")
},
'editor.semanticHighlighting.enabled': {
enum: [true, false, 'configuredByTheme'],

View file

@ -659,9 +659,21 @@ export class Cursor extends Disposable {
}, eventsCollector, source);
}
public replacePreviousChar(eventsCollector: ViewModelEventsCollector, text: string, replaceCharCnt: number, source?: string | null | undefined): void {
public compositionType(eventsCollector: ViewModelEventsCollector, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number, source?: string | null | undefined): void {
if (text.length === 0 && replacePrevCharCnt === 0 && replaceNextCharCnt === 0) {
// this edit is a no-op
if (positionDelta !== 0) {
// but it still wants to move the cursor
const newSelections = this.getSelections().map(selection => {
const position = selection.getPosition();
return new Selection(position.lineNumber, position.column + positionDelta, position.lineNumber, position.column + positionDelta);
});
this.setSelections(eventsCollector, source, newSelections, CursorChangeReason.NotSet);
}
return;
}
this._executeEdit(() => {
this._executeEditOperation(TypeOperations.replacePreviousChar(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replaceCharCnt));
this._executeEditOperation(TypeOperations.compositionType(this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), text, replacePrevCharCnt, replaceNextCharCnt, positionDelta));
}, eventsCollector, source);
}

View file

@ -258,34 +258,33 @@ export class TypeOperations {
return commands;
}
public static replacePreviousChar(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], txt: string, replaceCharCnt: number): EditOperationResult {
let commands: Array<ICommand | null> = [];
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
if (!selection.isEmpty()) {
// looks like https://github.com/microsoft/vscode/issues/2773
// where a cursor operation occurred before a canceled composition
// => ignore composition
commands[i] = null;
continue;
}
const pos = selection.getPosition();
const startColumn = Math.max(1, pos.column - replaceCharCnt);
const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, pos.column);
const oldText = model.getValueInRange(range);
if (oldText === txt) {
// => ignore composition that doesn't do anything
commands[i] = null;
continue;
}
commands[i] = new ReplaceCommand(range, txt);
}
public static compositionType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): EditOperationResult {
const commands = selections.map(selection => this._compositionType(model, selection, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta));
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: (prevEditOperationType !== EditOperationType.Typing),
shouldPushStackElementAfter: false
});
}
private static _compositionType(model: ITextModel, selection: Selection, text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number): ICommand | null {
if (!selection.isEmpty()) {
// looks like https://github.com/microsoft/vscode/issues/2773
// where a cursor operation occurred before a canceled composition
// => ignore composition
return null;
}
const pos = selection.getPosition();
const startColumn = Math.max(1, pos.column - replacePrevCharCnt);
const endColumn = Math.min(model.getLineMaxColumn(pos.lineNumber), pos.column + replaceNextCharCnt);
const range = new Range(pos.lineNumber, startColumn, pos.lineNumber, endColumn);
const oldText = model.getValueInRange(range);
if (oldText === text && positionDelta === 0) {
// => ignore composition that doesn't do anything
return null;
}
return new ReplaceCommandWithOffsetCursorState(range, text, 0, positionDelta);
}
private static _typeCommand(range: Range, text: string, keepPosition: boolean): ICommand {
if (keepPosition) {
return new ReplaceCommandWithoutChangingPosition(range, text, true);

View file

@ -700,6 +700,7 @@ export const enum Handler {
CompositionEnd = 'compositionEnd',
Type = 'type',
ReplacePreviousChar = 'replacePreviousChar',
CompositionType = 'compositionType',
Paste = 'paste',
Cut = 'cut',
}
@ -719,6 +720,16 @@ export interface ReplacePreviousCharPayload {
replaceCharCnt: number;
}
/**
* @internal
*/
export interface CompositionTypePayload {
text: string;
replacePrevCharCnt: number;
replaceNextCharCnt: number;
positionDelta: number;
}
/**
* @internal
*/

View file

@ -948,8 +948,8 @@ export class ViewModel extends Disposable implements IViewModel {
public type(text: string, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
}
public replacePreviousChar(text: string, replaceCharCnt: number, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.replacePreviousChar(eventsCollector, text, replaceCharCnt, source));
public compositionType(text: string, replacePrevCharCnt: number, replaceNextCharCnt: number, positionDelta: number, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.compositionType(eventsCollector, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source));
}
public paste(text: string, pasteOnNewLine: boolean, multicursorText?: string[] | null | undefined, source?: string | null | undefined): void {
this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));

View file

@ -43,6 +43,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
import { StandaloneThemeServiceImpl } from 'vs/editor/standalone/browser/standaloneThemeServiceImpl';
import { splitLines } from 'vs/base/common/strings';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ILogService } from 'vs/platform/log/common/log';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
@ -56,7 +57,7 @@ function withAllStandaloneServices<T extends IEditor>(domElement: HTMLElement, o
}
if (!services.has(IOpenerService)) {
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService)));
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService), services.get(ILogService)));
}
let result = callback(services);

View file

@ -32,7 +32,7 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ILabelService } from 'vs/platform/label/common/label';
import { IListService, ListService } from 'vs/platform/list/browser/listService';
import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log';
import { ConsoleLogger, ILogService, LogService } from 'vs/platform/log/common/log';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { INotificationService } from 'vs/platform/notification/common/notification';
@ -152,7 +152,7 @@ export module StaticServices {
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
export const logService = define(ILogService, () => new ConsoleLogService());
export const logService = define(ILogService, () => new LogService(new ConsoleLogger()));
export const undoRedoService = define(IUndoRedoService, (o) => new UndoRedoService(dialogService.get(o), notificationService.get(o)));

View file

@ -2079,16 +2079,16 @@ suite('Editor Controller - Regression tests', () => {
// Typing sennsei in Japanese - Hiragana
viewModel.type('', 'keyboard');
viewModel.replacePreviousChar('せ', 1);
viewModel.replacePreviousChar('せn', 1);
viewModel.replacePreviousChar('せん', 2);
viewModel.replacePreviousChar('せんs', 2);
viewModel.replacePreviousChar('せんせ', 3);
viewModel.replacePreviousChar('せんせ', 3);
viewModel.replacePreviousChar('せんせい', 3);
viewModel.replacePreviousChar('せんせい', 4);
viewModel.replacePreviousChar('せんせい', 4);
viewModel.replacePreviousChar('せんせい', 4);
viewModel.compositionType('せ', 1, 0, 0);
viewModel.compositionType('せn', 1, 0, 0);
viewModel.compositionType('せん', 2, 0, 0);
viewModel.compositionType('せんs', 2, 0, 0);
viewModel.compositionType('せんせ', 3, 0, 0);
viewModel.compositionType('せんせ', 3, 0, 0);
viewModel.compositionType('せんせい', 3, 0, 0);
viewModel.compositionType('せんせい', 4, 0, 0);
viewModel.compositionType('せんせい', 4, 0, 0);
viewModel.compositionType('せんせい', 4, 0, 0);
assert.strictEqual(model.getLineContent(1), 'せんせい');
assertCursor(viewModel, new Position(1, 5));
@ -5449,7 +5449,7 @@ suite('autoClosingPairs', () => {
// Typing ` + e on the mac US intl kb layout
viewModel.startComposition();
viewModel.type('`', 'keyboard');
viewModel.replacePreviousChar('è', 1, 'keyboard');
viewModel.compositionType('è', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), 'è');
@ -5470,8 +5470,8 @@ suite('autoClosingPairs', () => {
// Typing ` + e on the mac US intl kb layout
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '\'test\'');
@ -5550,8 +5550,8 @@ suite('autoClosingPairs', () => {
viewModel.startComposition();
viewModel.type('`', 'keyboard');
moveDown(editor, viewModel, true);
viewModel.replacePreviousChar('`', 1, 'keyboard');
viewModel.replacePreviousChar('`', 1, 'keyboard');
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
viewModel.compositionType('`', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '`hello\nworld');
@ -5575,14 +5575,14 @@ suite('autoClosingPairs', () => {
// Typing ' + space
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '\'\'');
// Typing one more ' + space
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '\'\'');
@ -5591,7 +5591,7 @@ suite('autoClosingPairs', () => {
viewModel.setSelections('test', [new Selection(1, 5, 1, 5)]);
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '\'abc\'');
@ -5601,7 +5601,7 @@ suite('autoClosingPairs', () => {
viewModel.setSelections('test', [new Selection(1, 10, 1, 10)]);
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '\'abc\'def \'\'');
@ -5611,7 +5611,7 @@ suite('autoClosingPairs', () => {
viewModel.setSelections('test', [new Selection(1, 1, 1, 1)]);
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
// No auto closing if it's after a word.
@ -5619,7 +5619,7 @@ suite('autoClosingPairs', () => {
viewModel.setSelections('test', [new Selection(1, 4, 1, 4)]);
viewModel.startComposition();
viewModel.type('\'', 'keyboard');
viewModel.replacePreviousChar('\'', 1, 'keyboard');
viewModel.compositionType('\'', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), 'abc\'');
@ -5640,7 +5640,7 @@ suite('autoClosingPairs', () => {
// Typing a + backspace
viewModel.startComposition();
viewModel.type('a', 'keyboard');
viewModel.replacePreviousChar('', 1, 'keyboard');
viewModel.compositionType('', 1, 0, 0, 'keyboard');
viewModel.endComposition('keyboard');
assert.strictEqual(model.getValue(), '{}');
});

View file

@ -151,9 +151,9 @@ function doCreateTest(description: string, inputStr: string, expectedStr: string
};
handler.onType((e) => {
console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replaceCharCnt);
console.log('type text: ' + e.text + ', replaceCharCnt: ' + e.replacePrevCharCnt);
let text = model.getModelLineContent(1);
let preText = text.substring(0, cursorOffset - e.replaceCharCnt);
let preText = text.substring(0, cursorOffset - e.replacePrevCharCnt);
let postText = text.substring(cursorOffset + cursorLength);
let midText = e.text;

View file

@ -134,8 +134,12 @@ suite('TextAreaState', () => {
let newState = TextAreaState.readFromTextArea(textArea);
let actual = TextAreaState.deduceInput(prevState, newState, couldBeEmojiInput);
assert.strictEqual(actual.text, expected);
assert.strictEqual(actual.replaceCharCnt, expectedCharReplaceCnt);
assert.deepStrictEqual(actual, {
text: expected,
replacePrevCharCnt: expectedCharReplaceCnt,
replaceNextCharCnt: 0,
positionDelta: 0,
});
textArea.dispose();
}
@ -503,6 +507,82 @@ suite('TextAreaState', () => {
);
});
function testDeduceAndroidCompositionInput(
prevState: TextAreaState | null,
value: string, selectionStart: number, selectionEnd: number,
expected: string, expectedReplacePrevCharCnt: number, expectedReplaceNextCharCnt: number, expectedPositionDelta: number): void {
prevState = prevState || TextAreaState.EMPTY;
let textArea = new MockTextAreaWrapper();
textArea._value = value;
textArea._selectionStart = selectionStart;
textArea._selectionEnd = selectionEnd;
let newState = TextAreaState.readFromTextArea(textArea);
let actual = TextAreaState.deduceAndroidCompositionInput(prevState, newState);
assert.deepStrictEqual(actual, {
text: expected,
replacePrevCharCnt: expectedReplacePrevCharCnt,
replaceNextCharCnt: expectedReplaceNextCharCnt,
positionDelta: expectedPositionDelta,
});
textArea.dispose();
}
test('Android composition input 1', () => {
testDeduceAndroidCompositionInput(
new TextAreaState(
'Microsoft',
4, 4,
null, null
),
'Microsoft',
4, 4,
'', 0, 0, 0,
);
});
test('Android composition input 2', () => {
testDeduceAndroidCompositionInput(
new TextAreaState(
'Microsoft',
4, 4,
null, null
),
'Microsoft',
0, 9,
'', 0, 0, 5,
);
});
test('Android composition input 3', () => {
testDeduceAndroidCompositionInput(
new TextAreaState(
'Microsoft',
0, 9,
null, null
),
'Microsoft\'s',
11, 11,
'\'s', 0, 0, 0,
);
});
test('Android backspace', () => {
testDeduceAndroidCompositionInput(
new TextAreaState(
'undefinedVariable',
2, 2,
null, null
),
'udefinedVariable',
1, 1,
'', 1, 0, 0,
);
});
suite('PagedScreenReaderStrategy', () => {
function testPagedScreenReaderStrategy(lines: string[], selection: Selection, expected: TextAreaState): void {

View file

@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
import { NullLogService } from 'vs/platform/log/common/log';
import { matchesScheme } from 'vs/platform/opener/common/opener';
suite('OpenerService', function () {
@ -30,13 +31,13 @@ suite('OpenerService', function () {
});
test('delegate to editorService, scheme:///fff', async function () {
const openerService = new OpenerService(editorService, NullCommandService);
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
await openerService.open(URI.parse('another:///somepath'));
assert.equal(editorService.lastInput!.options!.selection, undefined);
});
test('delegate to editorService, scheme:///fff#L123', async function () {
const openerService = new OpenerService(editorService, NullCommandService);
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
await openerService.open(URI.parse('file:///somepath#L23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@ -58,7 +59,7 @@ suite('OpenerService', function () {
});
test('delegate to editorService, scheme:///fff#123,123', async function () {
const openerService = new OpenerService(editorService, NullCommandService);
const openerService = new OpenerService(editorService, NullCommandService, new NullLogService());
await openerService.open(URI.parse('file:///somepath#23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@ -76,7 +77,7 @@ suite('OpenerService', function () {
});
test('delegate to commandsService, command:someid', async function () {
const openerService = new OpenerService(editorService, commandService);
const openerService = new OpenerService(editorService, commandService, new NullLogService());
const id = `aCommand${Math.random()}`;
CommandsRegistry.registerCommand(id, function () { });
@ -98,7 +99,7 @@ suite('OpenerService', function () {
});
test('links are protected by validators', async function () {
const openerService = new OpenerService(editorService, commandService);
const openerService = new OpenerService(editorService, commandService, new NullLogService());
openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) });
@ -109,7 +110,7 @@ suite('OpenerService', function () {
});
test('links validated by validators go to openers', async function () {
const openerService = new OpenerService(editorService, commandService);
const openerService = new OpenerService(editorService, commandService, new NullLogService());
openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) });
@ -128,7 +129,7 @@ suite('OpenerService', function () {
});
test('links validated by multiple validators', async function () {
const openerService = new OpenerService(editorService, commandService);
const openerService = new OpenerService(editorService, commandService, new NullLogService());
let v1 = 0;
openerService.registerValidator({
@ -165,7 +166,7 @@ suite('OpenerService', function () {
});
test('links invalidated by first validator do not continue validating', async function () {
const openerService = new OpenerService(editorService, commandService);
const openerService = new OpenerService(editorService, commandService, new NullLogService());
let v1 = 0;
openerService.registerValidator({

View file

@ -35,10 +35,10 @@ export function testCommand(
cursor.executeCommand(commandFactory(cursor.getSelection()), 'tests');
assert.deepEqual(model.getLinesContent(), expectedLines);
assert.deepStrictEqual(model.getLinesContent(), expectedLines);
let actualSelection = cursor.getSelection();
assert.deepEqual(actualSelection.toString(), expectedSelection.toString());
assert.deepStrictEqual(actualSelection.toString(), expectedSelection.toString());
});
model.dispose();

View file

@ -20,7 +20,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
let inverseEdits = model.applyEdits(edits, true);
// Assert edits produced expected result
assert.deepEqual(model.getValue(EndOfLinePreference.LF), expectedStr);
assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), expectedStr);
assertMirrorModels();
@ -28,7 +28,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
let inverseInverseEdits = model.applyEdits(inverseEdits, true);
// Assert the inverse edits brought back model to original state
assert.deepEqual(model.getValue(EndOfLinePreference.LF), originalStr);
assert.deepStrictEqual(model.getValue(EndOfLinePreference.LF), originalStr);
if (!inputEditsAreInvalid) {
const simplifyEdit = (edit: IIdentifiedSingleEditOperation) => {
@ -41,7 +41,7 @@ export function testApplyEditsWithSyncedModels(original: string[], edits: IIdent
};
};
// Assert the inverse of the inverse edits are the original edits
assert.deepEqual(inverseInverseEdits.map(simplifyEdit), edits.map(simplifyEdit));
assert.deepStrictEqual(inverseInverseEdits.map(simplifyEdit), edits.map(simplifyEdit));
}
assertMirrorModels();
@ -59,16 +59,16 @@ function assertOneDirectionLineMapping(model: TextModel, direction: AssertDocume
let line = 1, column = 1, previousIsCarriageReturn = false;
for (let offset = 0; offset <= allText.length; offset++) {
// The position coordinate system cannot express the position between \r and \n
let position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));
let position: Position = new Position(line, column + (previousIsCarriageReturn ? -1 : 0));
if (direction === AssertDocumentLineMappingDirection.OffsetToPosition) {
let actualPosition = model.getPositionAt(offset);
assert.equal(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);
assert.strictEqual(actualPosition.toString(), position.toString(), msg + ' - getPositionAt mismatch for offset ' + offset);
} else {
// The position coordinate system cannot express the position between \r and \n
let expectedOffset = offset + (previousIsCarriageReturn ? -1 : 0);
let expectedOffset: number = offset + (previousIsCarriageReturn ? -1 : 0);
let actualOffset = model.getOffsetAt(position);
assert.equal(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());
assert.strictEqual(actualOffset, expectedOffset, msg + ' - getOffsetAt mismatch for position ' + position.toString());
}
if (allText.charAt(offset) === '\n') {
@ -112,8 +112,8 @@ export function assertSyncedModels(text: string, callback: (model: TextModel, as
let assertMirrorModels = () => {
assertLineMapping(model, 'model');
assert.equal(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
assert.equal(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
assert.strictEqual(mirrorModel2.getText(), model.getValue(), 'mirror model 2 text OK');
assert.strictEqual(mirrorModel2.version, model.getVersionId(), 'mirror model 2 version OK');
};
callback(model, assertMirrorModels);

View file

@ -175,7 +175,7 @@ export interface IMenuService {
readonly _serviceBrand: undefined;
createMenu(id: MenuId, scopedKeybindingService: IContextKeyService): IMenu;
createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu;
}
export type ICommandsMap = Map<string, ICommandAction>;

View file

@ -17,7 +17,7 @@ import { IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
import { IBackupWorkspacesFormat, ISerializedWorkspace } from 'vs/platform/backup/node/backup';
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ConsoleLogMainService } from 'vs/platform/log/common/log';
import { ConsoleMainLogger, LogService } from 'vs/platform/log/common/log';
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { createHash } from 'crypto';
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
@ -111,7 +111,7 @@ flakySuite('BackupMainService', () => {
configService = new TestConfigurationService();
service = new class TestBackupMainService extends BackupMainService {
constructor() {
super(environmentService, configService, new ConsoleLogMainService());
super(environmentService, configService, new LogService(new ConsoleMainLogger()));
this.backupHome = backupHome;
this.workspacesJsonPath = backupWorkspacesPath;

View file

@ -8,22 +8,22 @@ import { INormalizedVersion, IParsedVersion, IReducedExtensionDescription, isVal
suite('Extension Version Validator', () => {
test('isValidVersionStr', () => {
assert.equal(isValidVersionStr('0.10.0-dev'), true);
assert.equal(isValidVersionStr('0.10.0'), true);
assert.equal(isValidVersionStr('0.10.1'), true);
assert.equal(isValidVersionStr('0.10.100'), true);
assert.equal(isValidVersionStr('0.11.0'), true);
assert.strictEqual(isValidVersionStr('0.10.0-dev'), true);
assert.strictEqual(isValidVersionStr('0.10.0'), true);
assert.strictEqual(isValidVersionStr('0.10.1'), true);
assert.strictEqual(isValidVersionStr('0.10.100'), true);
assert.strictEqual(isValidVersionStr('0.11.0'), true);
assert.equal(isValidVersionStr('x.x.x'), true);
assert.equal(isValidVersionStr('0.x.x'), true);
assert.equal(isValidVersionStr('0.10.0'), true);
assert.equal(isValidVersionStr('0.10.x'), true);
assert.equal(isValidVersionStr('^0.10.0'), true);
assert.equal(isValidVersionStr('*'), true);
assert.strictEqual(isValidVersionStr('x.x.x'), true);
assert.strictEqual(isValidVersionStr('0.x.x'), true);
assert.strictEqual(isValidVersionStr('0.10.0'), true);
assert.strictEqual(isValidVersionStr('0.10.x'), true);
assert.strictEqual(isValidVersionStr('^0.10.0'), true);
assert.strictEqual(isValidVersionStr('*'), true);
assert.equal(isValidVersionStr('0.x.x.x'), false);
assert.equal(isValidVersionStr('0.10'), false);
assert.equal(isValidVersionStr('0.10.'), false);
assert.strictEqual(isValidVersionStr('0.x.x.x'), false);
assert.strictEqual(isValidVersionStr('0.10'), false);
assert.strictEqual(isValidVersionStr('0.10.'), false);
});
test('parseVersion', () => {
@ -31,7 +31,7 @@ suite('Extension Version Validator', () => {
const actual = parseVersion(version);
const expected: IParsedVersion = { hasCaret, hasGreaterEquals, majorBase, majorMustEqual, minorBase, minorMustEqual, patchBase, patchMustEqual, preRelease };
assert.deepEqual(actual, expected, 'parseVersion for ' + version);
assert.deepStrictEqual(actual, expected, 'parseVersion for ' + version);
}
assertParseVersion('0.10.0-dev', false, false, 0, true, 10, true, 0, true, '-dev');
@ -56,7 +56,7 @@ suite('Extension Version Validator', () => {
function assertNormalizeVersion(version: string, majorBase: number, majorMustEqual: boolean, minorBase: number, minorMustEqual: boolean, patchBase: number, patchMustEqual: boolean, isMinimum: boolean): void {
const actual = normalizeVersion(parseVersion(version));
const expected: INormalizedVersion = { majorBase, majorMustEqual, minorBase, minorMustEqual, patchBase, patchMustEqual, isMinimum };
assert.deepEqual(actual, expected, 'parseVersion for ' + version);
assert.deepStrictEqual(actual, expected, 'parseVersion for ' + version);
}
assertNormalizeVersion('0.10.0-dev', 0, true, 10, true, 0, true, false);
@ -80,7 +80,7 @@ suite('Extension Version Validator', () => {
test('isValidVersion', () => {
function testIsValidVersion(version: string, desiredVersion: string, expectedResult: boolean): void {
let actual = isValidVersion(version, desiredVersion);
assert.equal(actual, expectedResult, 'extension - vscode: ' + version + ', desiredVersion: ' + desiredVersion + ' should be ' + expectedResult);
assert.strictEqual(actual, expectedResult, 'extension - vscode: ' + version + ', desiredVersion: ' + desiredVersion + ' should be ' + expectedResult);
}
testIsValidVersion('0.10.0-dev', 'x.x.x', true);
@ -213,7 +213,7 @@ suite('Extension Version Validator', () => {
let reasons: string[] = [];
let actual = isValidExtensionVersion(version, desc, reasons);
assert.equal(actual, expectedResult, 'version: ' + version + ', desiredVersion: ' + desiredVersion + ', desc: ' + JSON.stringify(desc) + ', reasons: ' + JSON.stringify(reasons));
assert.strictEqual(actual, expectedResult, 'version: ' + version + ', desiredVersion: ' + desiredVersion + ', desc: ' + JSON.stringify(desc) + ', reasons: ' + JSON.stringify(reasons));
}
function testIsInvalidExtensionVersion(version: string, desiredVersion: string, isBuiltin: boolean, hasMain: boolean): void {

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, DEFAULT_LOG_LEVEL, LogLevel, LogServiceAdapter } from 'vs/platform/log/common/log';
import { DEFAULT_LOG_LEVEL, LogLevel, AdapterLogger, ILogger } from 'vs/platform/log/common/log';
interface IAutomatedWindow {
codeAutomationLog(type: string, args: any[]): void;
@ -14,14 +14,12 @@ interface IAutomatedWindow {
* an automation such as playwright. We expect a global codeAutomationLog
* to be defined that we can use to log to.
*/
export class ConsoleLogInAutomationService extends LogServiceAdapter implements ILogService {
export class ConsoleLogInAutomationLogger extends AdapterLogger implements ILogger {
declare codeAutomationLog: any;
declare readonly _serviceBrand: undefined;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super({ consoleLog: (type, args) => this.consoleLog(type, args) }, logLevel);
super({ log: (type, args) => this.consoleLog(type, args) }, logLevel);
}
private consoleLog(type: string, args: any[]): void {

View file

@ -3,14 +3,14 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, AbstractLogger, DEFAULT_LOG_LEVEL, ILogger } from 'vs/platform/log/common/log';
interface ILog {
level: LogLevel;
args: any[];
}
function getLogFunction(logger: ILogService, level: LogLevel): Function {
function getLogFunction(logger: ILogger, level: LogLevel): Function {
switch (level) {
case LogLevel.Trace: return logger.trace;
case LogLevel.Debug: return logger.debug;
@ -22,11 +22,11 @@ function getLogFunction(logger: ILogService, level: LogLevel): Function {
}
}
export class BufferLogService extends AbstractLogService implements ILogService {
export class BufferLogService extends AbstractLogger implements ILogService {
declare readonly _serviceBrand: undefined;
private buffer: ILog[] = [];
private _logger: ILogService | undefined = undefined;
private _logger: ILogger | undefined = undefined;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
@ -38,7 +38,7 @@ export class BufferLogService extends AbstractLogService implements ILogService
}));
}
set logger(logger: ILogService) {
set logger(logger: ILogger) {
this._logger = logger;
for (const { level, args } of this.buffer) {

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, AbstractLogger, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { ByteSize, FileOperationError, FileOperationResult, IFileService, whenProviderRegistered } from 'vs/platform/files/common/files';
import { Queue } from 'vs/base/common/async';
@ -15,9 +15,7 @@ import { BufferLogService } from 'vs/platform/log/common/bufferLog';
const MAX_FILE_SIZE = 5 * ByteSize.MB;
export class FileLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
export class FileLogger extends AbstractLogger implements ILogger {
private readonly initializePromise: Promise<void>;
private readonly queue: Queue<void>;
@ -181,7 +179,7 @@ export class FileLoggerService extends Disposable implements ILoggerService {
if (!logger) {
logger = new BufferLogService(this.logService.getLevel());
this.loggers.set(resource.toString(), logger);
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogService, basename(resource), resource, this.logService.getLevel()));
whenProviderRegistered(resource, this.fileService).then(() => (<BufferLogService>logger).logger = this.instantiationService.createInstance(FileLogger, basename(resource), resource, this.logService.getLevel()));
}
return logger;
}

View file

@ -59,7 +59,7 @@ export interface ILoggerService {
getLogger(file: URI): ILogger;
}
export abstract class AbstractLogService extends Disposable {
export abstract class AbstractLogger extends Disposable {
private level: LogLevel = DEFAULT_LOG_LEVEL;
private readonly _onDidChangeLogLevel: Emitter<LogLevel> = this._register(new Emitter<LogLevel>());
@ -78,9 +78,8 @@ export abstract class AbstractLogService extends Disposable {
}
export class ConsoleLogMainService extends AbstractLogService implements ILogService {
export class ConsoleMainLogger extends AbstractLogger implements ILogger {
declare readonly _serviceBrand: undefined;
private useColors: boolean;
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
@ -159,9 +158,7 @@ export class ConsoleLogMainService extends AbstractLogService implements ILogSer
}
export class ConsoleLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
export class ConsoleLogger extends AbstractLogger implements ILogger {
constructor(logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
@ -213,48 +210,46 @@ export class ConsoleLogService extends AbstractLogService implements ILogService
}
}
export class LogServiceAdapter extends AbstractLogService implements ILogService {
export class AdapterLogger extends AbstractLogger implements ILogger {
declare readonly _serviceBrand: undefined;
constructor(private readonly adapter: { consoleLog: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
constructor(private readonly adapter: { log: (type: string, args: any[]) => void }, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super();
this.setLevel(logLevel);
}
trace(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this.adapter.consoleLog('trace', [this.extractMessage(message), ...args]);
this.adapter.log('trace', [this.extractMessage(message), ...args]);
}
}
debug(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this.adapter.consoleLog('debug', [this.extractMessage(message), ...args]);
this.adapter.log('debug', [this.extractMessage(message), ...args]);
}
}
info(message: string, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this.adapter.consoleLog('info', [this.extractMessage(message), ...args]);
this.adapter.log('info', [this.extractMessage(message), ...args]);
}
}
warn(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this.adapter.consoleLog('warn', [this.extractMessage(message), ...args]);
this.adapter.log('warn', [this.extractMessage(message), ...args]);
}
}
error(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this.adapter.consoleLog('error', [this.extractMessage(message), ...args]);
this.adapter.log('error', [this.extractMessage(message), ...args]);
}
}
critical(message: string | Error, ...args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this.adapter.consoleLog('critical', [this.extractMessage(message), ...args]);
this.adapter.log('critical', [this.extractMessage(message), ...args]);
}
}
@ -275,19 +270,17 @@ export class LogServiceAdapter extends AbstractLogService implements ILogService
}
}
export class ConsoleLogInMainService extends LogServiceAdapter implements ILogService {
declare readonly _serviceBrand: undefined;
export class ConsoleLogInMainService extends AdapterLogger implements ILogger {
constructor(client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) {
super({ consoleLog: (type, args) => client.consoleLog(type, args) }, logLevel);
super({ log: (type, args) => client.consoleLog(type, args) }, logLevel);
}
}
export class MultiplexLogService extends AbstractLogService implements ILogService {
export class MultiplexLogService extends AbstractLogger implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private readonly logServices: ReadonlyArray<ILogService>) {
constructor(private readonly logServices: ReadonlyArray<ILogger>) {
super();
if (logServices.length) {
this.setLevel(logServices[0].getLevel());
@ -350,52 +343,52 @@ export class MultiplexLogService extends AbstractLogService implements ILogServi
}
}
export class DelegatedLogService extends Disposable implements ILogService {
export class LogService extends Disposable implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private logService: ILogService) {
constructor(private logger: ILogger) {
super();
this._register(logService);
this._register(logger);
}
get onDidChangeLogLevel(): Event<LogLevel> {
return this.logService.onDidChangeLogLevel;
return this.logger.onDidChangeLogLevel;
}
setLevel(level: LogLevel): void {
this.logService.setLevel(level);
this.logger.setLevel(level);
}
getLevel(): LogLevel {
return this.logService.getLevel();
return this.logger.getLevel();
}
trace(message: string, ...args: any[]): void {
this.logService.trace(message, ...args);
this.logger.trace(message, ...args);
}
debug(message: string, ...args: any[]): void {
this.logService.debug(message, ...args);
this.logger.debug(message, ...args);
}
info(message: string, ...args: any[]): void {
this.logService.info(message, ...args);
this.logger.info(message, ...args);
}
warn(message: string, ...args: any[]): void {
this.logService.warn(message, ...args);
this.logger.warn(message, ...args);
}
error(message: string | Error, ...args: any[]): void {
this.logService.error(message, ...args);
this.logger.error(message, ...args);
}
critical(message: string | Error, ...args: any[]): void {
this.logService.critical(message, ...args);
this.logger.critical(message, ...args);
}
flush(): void {
this.logService.flush();
this.logger.flush();
}
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log';
import { LogLevel, ILogService, LogService } from 'vs/platform/log/common/log';
import { Event } from 'vs/base/common/event';
export class LoggerChannel implements IServerChannel {
@ -72,7 +72,7 @@ export class LoggerChannelClient {
}
}
export class FollowerLogService extends DelegatedLogService implements ILogService {
export class FollowerLogService extends LogService implements ILogService {
declare readonly _serviceBrand: undefined;
constructor(private parent: LoggerChannelClient, logService: ILogService) {

View file

@ -8,8 +8,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { basename, extname, dirname } from 'vs/base/common/resources';
import { Schemas } from 'vs/base/common/network';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { FileLogger } from 'vs/platform/log/common/fileLog';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { IFileService } from 'vs/platform/files/common/files';
export class LoggerService extends Disposable implements ILoggerService {
@ -32,9 +32,9 @@ export class LoggerService extends Disposable implements ILoggerService {
if (resource.scheme === Schemas.file) {
const baseName = basename(resource);
const ext = extname(resource);
logger = new SpdLogService(baseName.substring(0, baseName.length - ext.length), dirname(resource).fsPath, this.logService.getLevel());
logger = new SpdLogLogger(baseName.substring(0, baseName.length - ext.length), dirname(resource).fsPath, this.logService.getLevel());
} else {
logger = new FileLogService(basename(resource), resource, this.logService.getLevel(), this.fileService);
logger = new FileLogger(basename(resource), resource, this.logService.getLevel(), this.fileService);
}
this.loggers.set(resource.toString(), logger);
}

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as path from 'vs/base/common/path';
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { LogLevel, AbstractLogger, ILogger } from 'vs/platform/log/common/log';
import * as spdlog from 'spdlog';
import { ByteSize } from 'vs/platform/files/common/files';
@ -43,9 +43,7 @@ function log(logger: spdlog.RotatingLogger, level: LogLevel, message: string): v
}
}
export class SpdLogService extends AbstractLogService implements ILogService {
declare readonly _serviceBrand: undefined;
export class SpdLogLogger extends AbstractLogger implements ILogger {
private buffer: ILog[] = [];
private _loggerCreationPromise: Promise<void> | undefined = undefined;

View file

@ -3,13 +3,12 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { LogLevel, ILoggerService, AbstractLogService, DEFAULT_LOG_LEVEL, ILogger } from 'vs/platform/log/common/log';
import { LogLevel, ILoggerService, AbstractLogger, DEFAULT_LOG_LEVEL, ILogger } from 'vs/platform/log/common/log';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
class TestTelemetryLogger extends AbstractLogService implements ILogger {
declare readonly _serviceBrand: undefined;
class TestTelemetryLogger extends AbstractLogger implements ILogger {
public logs: string[] = [];

View file

@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
import { AbstractLogService, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { AbstractLogger, ILoggerService, ILogger } from 'vs/platform/log/common/log';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export class UserDataSyncLogService extends AbstractLogService implements IUserDataSyncLogService {
export class UserDataSyncLogService extends AbstractLogger implements IUserDataSyncLogService {
declare readonly _serviceBrand: undefined;
private readonly logger: ILogger;

View file

@ -81,14 +81,14 @@ export function isFileToOpen(uriToOpen: IWindowOpenable): uriToOpen is IFileToOp
return !!(uriToOpen as IFileToOpen).fileUri;
}
export type MenuBarVisibility = 'default' | 'visible' | 'toggle' | 'hidden' | 'compact';
export type MenuBarVisibility = 'classic' | 'visible' | 'toggle' | 'hidden' | 'compact';
export function getMenuBarVisibility(configurationService: IConfigurationService): MenuBarVisibility {
const titleBarStyle = getTitleBarStyle(configurationService);
const menuBarVisibility = configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility');
const menuBarVisibility = configurationService.getValue<MenuBarVisibility | 'default'>('window.menuBarVisibility');
if (titleBarStyle === 'native' && menuBarVisibility === 'compact') {
return 'default';
if (menuBarVisibility === 'default' || (titleBarStyle === 'native' && menuBarVisibility === 'compact')) {
return 'classic';
} else {
return menuBarVisibility;
}

View file

@ -1073,6 +1073,7 @@ declare module 'vscode' {
custom?: { [key: string]: any; };
}
// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
export interface NotebookCell {
readonly index: number;
readonly notebook: NotebookDocument;
@ -1082,6 +1083,7 @@ declare module 'vscode' {
readonly language: string;
/** @deprecated use WorkspaceEdit.replaceCellOutput */
outputs: CellOutput[];
// readonly outputs2: NotebookCellOutput[];
/** @deprecated use WorkspaceEdit.replaceCellMetadata */
metadata: NotebookCellMetadata;
}
@ -1318,6 +1320,7 @@ declare module 'vscode' {
readonly visibleRanges: ReadonlyArray<NotebookCellRange>;
}
// todo@API support ids https://github.com/jupyter/enhancement-proposals/blob/master/62-cell-id/cell-id.md
export interface NotebookCellData {
readonly cellKind: CellKind;
readonly source: string;
@ -1482,9 +1485,11 @@ declare module 'vscode' {
constructor(mime: string, value: unknown, metadata?: Record<string, string | number | boolean>);
}
//TODO@jrieken add id?
// @jrieken
//TODO@API add execution count to cell output?
export class NotebookCellOutput {
readonly id: string;
readonly outputs: NotebookCellOutputItem[];
constructor(outputs: NotebookCellOutputItem[]);
@ -1501,8 +1506,14 @@ declare module 'vscode' {
replaceNotebookMetadata(uri: Uri, value: NotebookDocumentMetadata): void;
replaceNotebookCells(uri: Uri, start: number, end: number, cells: NotebookCellData[], metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellMetadata(uri: Uri, index: number, cellMetadata: NotebookCellMetadata, metadata?: WorkspaceEditEntryMetadata): void;
replaceNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void;
appendNotebookCellOutput(uri: Uri, index: number, outputs: NotebookCellOutput[], metadata?: WorkspaceEditEntryMetadata): void;
appendNotebookCellOutput(uri: Uri, index: number, outputs: (NotebookCellOutput | CellOutput)[], metadata?: WorkspaceEditEntryMetadata): void;
// TODO@api
// https://jupyter-protocol.readthedocs.io/en/latest/messaging.html#update-display-data
// replaceNotebookCellOutput(uri: Uri, index: number, outputId:string, outputs: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
// appendNotebookCellOutput(uri: Uri, index: number, outputId:string, outputs: NotebookCellOutputItem[], metadata?: WorkspaceEditEntryMetadata): void;
}
export interface NotebookEditorEdit {

View file

@ -26,10 +26,9 @@ import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
// this class contains the commands that the CLI server is reying on
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents, options: { allowTunneling?: boolean }) {
// TODO: discuss martin, ben where to put this
CommandsRegistry.registerCommand('_remoteCLI.openExternal', function (accessor: ServicesAccessor, uri: UriComponents | string) {
const openerService = accessor.get(IOpenerService);
openerService.open(URI.revive(uri), { openExternal: true, allowTunneling: options?.allowTunneling === true });
openerService.open(isString(uri) ? uri : URI.revive(uri), { openExternal: true, allowTunneling: true });
});
interface ManageExtensionsArgs {

View file

@ -8,14 +8,14 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { UriComponents, URI } from 'vs/base/common/uri';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { FileLogger } from 'vs/platform/log/common/fileLog';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { basename } from 'vs/base/common/path';
@extHostNamedCustomer(MainContext.MainThreadLog)
export class MainThreadLogService implements MainThreadLogShape {
private readonly _loggers = new Map<string, FileLogService>();
private readonly _loggers = new Map<string, FileLogger>();
private readonly _logListener: IDisposable;
constructor(
@ -40,7 +40,7 @@ export class MainThreadLogService implements MainThreadLogShape {
const uri = URI.revive(file);
let logger = this._loggers.get(uri.toString());
if (!logger) {
logger = this._instaService.createInstance(FileLogService, basename(file.path), URI.revive(file), this._logService.getLevel());
logger = this._instaService.createInstance(FileLogger, basename(file.path), URI.revive(file), this._logService.getLevel());
this._loggers.set(uri.toString(), logger);
}
logger.log(level, message);

View file

@ -35,7 +35,7 @@ import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages';
import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import { ExtHostProgress } from 'vs/workbench/api/common/extHostProgress';
import { ExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
import { createExtHostQuickOpen } from 'vs/workbench/api/common/extHostQuickOpen';
import { ExtHostSCM } from 'vs/workbench/api/common/extHostSCM';
import { ExtHostStatusBar } from 'vs/workbench/api/common/extHostStatusBar';
import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
@ -147,7 +147,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation));
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, createExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
const extHostComment = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands, extHostDocuments));
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, IDisplayOutput, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { addIdToOutput, CellEditType, ICellEditOperation, ICellOutputEdit, ITransformedDisplayOutputDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import type * as vscode from 'vscode';
function es5ClassCompat(target: Function): any {
@ -662,7 +662,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
append,
outputs: outputs.map(output => {
if (NotebookCellOutput.isNotebookCellOutput(output)) {
return addIdToOutput(output.toJSON());
return output.toJSON();
} else {
return addIdToOutput(output);
}
@ -2826,9 +2826,11 @@ export class NotebookCellOutput {
return obj instanceof NotebookCellOutput;
}
readonly id: string = generateUuid();
constructor(readonly outputs: NotebookCellOutputItem[]) { }
toJSON(): IDisplayOutput {
toJSON(): ITransformedDisplayOutputDto {
let data: { [key: string]: unknown; } = {};
let custom: { [key: string]: unknown; } = {};
let hasMetadata = false;
@ -2841,6 +2843,7 @@ export class NotebookCellOutput {
}
}
return {
outputId: this.id,
outputKind: CellOutputKind.Rich,
data,
metadata: hasMetadata ? { custom } : undefined

View file

@ -156,8 +156,10 @@ export class CLIServerBase {
}
private async openExternal(data: OpenExternalCommandPipeArgs, res: http.ServerResponse) {
for (const uri of data.uris) {
await this._commands.executeCommand('_remoteCLI.openExternal', URI.parse(uri), { allowTunneling: true });
for (const uriString of data.uris) {
const uri = URI.parse(uriString);
const urioOpen = uri.scheme === 'file' ? uri : uriString; // workaround for #112577
await this._commands.executeCommand('_remoteCLI.openExternal', urioOpen);
}
res.writeHead(200);
res.end();

View file

@ -23,7 +23,7 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
const that = this;
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: { filename: string; }, isMain: any) {
node_module._load = function load(request: string, parent: { filename: string; }, isMain: boolean) {
for (let alternativeModuleName of that._alternatives) {
let alternative = alternativeModuleName(request);
if (alternative) {

View file

@ -3,21 +3,21 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
import { ILogService, LogService, LogLevel } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { Schemas } from 'vs/base/common/network';
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog';
import { dirname } from 'vs/base/common/resources';
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
export class ExtHostLogService extends LogService implements ILogService, ExtHostLogServiceShape {
constructor(
@IExtHostInitDataService initData: IExtHostInitDataService,
) {
if (initData.logFile.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); }
super(new SpdLogService(ExtensionHostLogFileName, dirname(initData.logFile).fsPath, initData.logLevel));
super(new SpdLogLogger(ExtensionHostLogFileName, dirname(initData.logFile).fsPath, initData.logLevel));
}
$setLevel(level: LogLevel): void {

View file

@ -3,13 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { ILogService, LogLevel, AbstractLogger } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { UriComponents } from 'vs/base/common/uri';
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
export class ExtHostLogService extends AbstractLogger implements ILogService, ExtHostLogServiceShape {
declare readonly _serviceBrand: undefined;

View file

@ -180,7 +180,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
windowBorder: false,
menuBar: {
visibility: 'default' as MenuBarVisibility,
visibility: 'classic' as MenuBarVisibility,
toggled: false
},
@ -321,8 +321,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
if (visible !== this.state.menuBar.toggled) {
this.state.menuBar.toggled = visible;
if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default')) {
// Propagate to grid
// The menu bar toggles the title bar in web because it does not need to be shown for window controls only
if (isWeb && this.state.menuBar.visibility === 'toggle') {
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
}
// The menu bar toggles the title bar in full screen for toggle and classic settings
else if (this.state.fullscreen && (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'classic')) {
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
}
@ -884,19 +889,35 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
isVisible(part: Parts): boolean {
switch (part) {
case Parts.TITLEBAR_PART:
// Using the native title bar, don't ever show the custom one
if (getTitleBarStyle(this.configurationService) === 'native') {
return false;
} else if (!this.state.fullscreen && !isWeb) {
return true;
} else if (isMacintosh && isNative) {
return false;
} else if (this.state.menuBar.visibility === 'visible') {
return true;
} else if (this.state.menuBar.visibility === 'toggle' || this.state.menuBar.visibility === 'default') {
return this.state.menuBar.toggled;
}
return false;
// macOS desktop does not need a title bar when full screen
if (isMacintosh && isNative) {
return !this.state.fullscreen;
}
// non-fullscreen native must show the title bar
if (isNative && !this.state.fullscreen) {
return true;
}
// remaining behavior is based on menubar visibility
switch (this.state.menuBar.visibility) {
case 'classic':
return !this.state.fullscreen || this.state.menuBar.toggled;
case 'compact':
case 'hidden':
return false;
case 'toggle':
return this.state.menuBar.toggled;
case 'visible':
return true;
default:
return isWeb ? false : !this.state.fullscreen || this.state.menuBar.toggled;
}
case Parts.SIDEBAR_PART:
return !this.state.sideBar.hidden;
case Parts.PANEL_PART:
@ -1560,16 +1581,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
toggleMenuBar(): void {
let currentVisibilityValue = getMenuBarVisibility(this.configurationService);
if (typeof currentVisibilityValue !== 'string') {
currentVisibilityValue = 'default';
currentVisibilityValue = 'classic';
}
let newVisibilityValue: string;
if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'default') {
if (currentVisibilityValue === 'visible' || currentVisibilityValue === 'classic') {
newVisibilityValue = 'toggle';
} else if (currentVisibilityValue === 'compact') {
newVisibilityValue = 'hidden';
} else {
newVisibilityValue = (isWeb && currentVisibilityValue === 'hidden') ? 'compact' : 'default';
newVisibilityValue = (isWeb && currentVisibilityValue === 'hidden') ? 'compact' : 'classic';
}
this.configurationService.updateValue(Storage.MENU_VISIBILITY, newVisibilityValue);

View file

@ -6,8 +6,8 @@
import { mark } from 'vs/base/common/performance';
import { domContentLoaded, detectFullscreen, getCookieValue } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService, ConsoleLogService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log';
import { ConsoleLogInAutomationService } from 'vs/platform/log/browser/log';
import { ILogService, ConsoleLogger, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log';
import { ConsoleLogInAutomationLogger } from 'vs/platform/log/browser/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { Workbench } from 'vs/workbench/browser/workbench';
@ -36,7 +36,7 @@ import type { IWorkbenchConstructionOptions, IWorkspace, IWorkbench } from 'vs/w
import { BrowserStorageService } from 'vs/platform/storage/browser/storageService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { FileLogger } from 'vs/platform/log/common/fileLog';
import { toLocalISOString } from 'vs/base/common/date';
import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows';
import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces';
@ -247,10 +247,10 @@ class BrowserMain extends Disposable {
}
logService.logger = new MultiplexLogService(coalesce([
new ConsoleLogService(logService.getLevel()),
new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService),
new ConsoleLogger(logService.getLevel()),
new FileLogger('window', environmentService.logFile, logService.getLevel(), fileService),
// Extension development test CLI: forward everything to test runner
environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationService(logService.getLevel()) : undefined
environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI ? new ConsoleLogInAutomationLogger(logService.getLevel()) : undefined
]));
})();

View file

@ -372,32 +372,36 @@ import { isStandalone } from 'vs/base/browser/browser';
},
'window.menuBarVisibility': {
'type': 'string',
'enum': ['default', 'visible', 'toggle', 'hidden', 'compact'],
'enum': ['classic', 'visible', 'toggle', 'hidden', 'compact'],
'markdownEnumDescriptions': [
nls.localize('window.menuBarVisibility.default', "Menu is only hidden in full screen mode."),
nls.localize('window.menuBarVisibility.visible', "Menu is always visible even in full screen mode."),
nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed via Alt key."),
nls.localize('window.menuBarVisibility.classic', "Menu is displayed at the top of the window and only hidden in full screen mode."),
nls.localize('window.menuBarVisibility.visible', "Menu is always visible at the top of the window even in full screen mode."),
isMacintosh ?
nls.localize('window.menuBarVisibility.toggle.mac', "Menu is hidden but can be displayed at the top of the window by executing the `Focus Application Menu` command.") :
nls.localize('window.menuBarVisibility.toggle', "Menu is hidden but can be displayed at the top of the window via the Alt key."),
nls.localize('window.menuBarVisibility.hidden', "Menu is always hidden."),
nls.localize('window.menuBarVisibility.compact', "Menu is displayed as a compact button in the sidebar. This value is ignored when `#window.titleBarStyle#` is `native`.")
],
'default': isWeb ? 'compact' : 'default',
'default': isWeb ? 'compact' : 'classic',
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. By default, the menu bar will be visible, unless the window is full screen."),
'markdownDescription': isMacintosh ?
nls.localize('menuBarVisibility.mac', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and executing `Focus Application Menu` will show it. A setting of 'compact' will move the menu into the sidebar.") :
nls.localize('menuBarVisibility', "Control the visibility of the menu bar. A setting of 'toggle' means that the menu bar is hidden and a single press of the Alt key will show it. A setting of 'compact' will move the menu into the sidebar."),
'included': isWindows || isLinux || isWeb
},
'window.enableMenuBarMnemonics': {
'type': 'boolean',
'default': !isMacintosh,
'default': true,
'scope': ConfigurationScope.APPLICATION,
'description': nls.localize('enableMenuBarMnemonics', "Controls whether the main menus can be opened via Alt-key shortcuts. Disabling mnemonics allows to bind these Alt-key shortcuts to editor commands instead."),
'included': isWindows || isLinux || isWeb
'included': isWindows || isLinux
},
'window.customMenuBarAltFocus': {
'type': 'boolean',
'default': !isMacintosh,
'default': true,
'scope': ConfigurationScope.APPLICATION,
'markdownDescription': nls.localize('customMenuBarAltFocus', "Controls whether the menu bar will be focused by pressing the Alt-key. This setting has no effect on toggling the menu bar with the Alt-key."),
'included': isWindows || isLinux || isWeb
'included': isWindows || isLinux
},
'window.openFilesInNewWindow': {
'type': 'string',

View file

@ -13,7 +13,7 @@ import { ITextMateService } from 'vs/workbench/services/textMate/common/textMate
import { IModelService } from 'vs/editor/common/services/modelService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { URI } from 'vs/base/common/uri';
import { createRotatingLogger } from 'vs/platform/log/node/spdlogService';
import { createRotatingLogger } from 'vs/platform/log/node/spdlogLog';
import { generateUuid } from 'vs/base/common/uuid';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { ITextModel } from 'vs/editor/common/model';

View file

@ -364,6 +364,10 @@ export function registerCommands(): void {
handler: async (accessor: ServicesAccessor, session: IDebugSession) => {
const debugService = accessor.get(IDebugService);
const editorService = accessor.get(IEditorService);
const stoppedChildSession = debugService.getModel().getSessions().find(s => s.parentSession === session && s.state === State.Stopped);
if (stoppedChildSession && session.state !== State.Stopped) {
session = stoppedChildSession;
}
await debugService.focusStackFrame(undefined, undefined, session, true);
const stackFrame = debugService.getViewModel().focusedStackFrame;
if (stackFrame) {

View file

@ -108,17 +108,17 @@ export class DebugService implements IDebugService {
this.adapterManager = this.instantiationService.createInstance(AdapterManager);
this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this.adapterManager);
this.toDispose.push(this.configurationManager);
this.debugStorage = this.instantiationService.createInstance(DebugStorage);
contextKeyService.bufferChangeEvents(() => {
this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService);
this.debugUx.set((this.adapterManager.hasDebuggers() && !!this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
this.debugUx.set(this.debugStorage.loadDebugUxState());
this.breakpointsExist = CONTEXT_BREAKPOINTS_EXIST.bindTo(contextKeyService);
});
this.debugStorage = this.instantiationService.createInstance(DebugStorage);
this.model = this.instantiationService.createInstance(DebugModel, this.debugStorage);
this.telemetry = this.instantiationService.createInstance(DebugTelemetry, this.model);
const setBreakpointsExistContext = () => this.breakpointsExist.set(!!(this.model.getBreakpoints().length || this.model.getDataBreakpoints().length || this.model.getFunctionBreakpoints().length));
@ -154,7 +154,9 @@ export class DebugService implements IDebugService {
this.onStateChange();
}));
this.toDispose.push(Event.any(this.adapterManager.onDidRegisterDebugger, this.configurationManager.onDidSelectConfiguration)(() => {
this.debugUx.set(!!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.adapterManager.hasDebuggers())) ? 'default' : 'simple');
const debugUxValue = !!(this.state !== State.Inactive || (this.configurationManager.selectedConfiguration.name && this.adapterManager.hasDebuggers())) ? 'default' : 'simple';
this.debugUx.set(debugUxValue);
this.debugStorage.storeDebugUxState(debugUxValue);
}));
this.toDispose.push(this.model.onDidChangeCallStack(() => {
const numberOfSessions = this.model.getSessions().filter(s => !s.parentSession).length;
@ -240,7 +242,9 @@ export class DebugService implements IDebugService {
this.debugState.set(getStateLabel(state));
this.inDebugMode.set(state !== State.Inactive);
// Only show the simple ux if debug is not yet started and if no launch.json exists
this.debugUx.set(((state !== State.Inactive && state !== State.Initializing) || (this.adapterManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple');
const debugUxValue = ((state !== State.Inactive && state !== State.Initializing) || (this.adapterManager.hasDebuggers() && this.configurationManager.selectedConfiguration.name)) ? 'default' : 'simple';
this.debugUx.set(debugUxValue);
this.debugStorage.storeDebugUxState(debugUxValue);
});
this.previousState = state;
this._onDidChangeState.fire(state);

View file

@ -840,7 +840,8 @@ export class DebugSession implements IDebugSession {
await promises.topCallStack;
focus();
await promises.wholeCallStack;
if (!this.debugService.getViewModel().focusedStackFrame) {
const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame;
if (!focusedStackFrame || !focusedStackFrame.source || focusedStackFrame.source.presentationHint === 'deemphasize') {
// The top stack frame can be deemphesized so try to focus again #68616
focus();
}

View file

@ -12,7 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IAction } from 'vs/base/common/actions';
import { Action, IAction } from 'vs/base/common/actions';
import { withUndefinedAsNull } from 'vs/base/common/types';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
@ -109,8 +109,9 @@ export class DebugTaskRunner {
await this.viewsService.openView(Constants.MARKERS_VIEW_ID, true);
return Promise.resolve(TaskRunResult.Failure);
} catch (err) {
await onError(err.message, [this.taskService.configureAction()]);
return TaskRunResult.Failure;
let debugAnyway = false;
await onError(err.message, [new Action('debug.debugAnyway', nls.localize('debugAnyway', "Debug Anyway"), undefined, true, async () => { debugAnyway = true; }), this.taskService.configureAction()]);
return debugAnyway ? TaskRunResult.Success : TaskRunResult.Failure;
}
}
@ -184,8 +185,8 @@ export class DebugTaskRunner {
setTimeout(() => {
if (!taskStarted) {
const errorMessage = typeof taskId === 'string'
? nls.localize('taskNotTrackedWithTaskId', "The specified task cannot be tracked.")
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked.", JSON.stringify(taskId));
? nls.localize('taskNotTrackedWithTaskId', "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined.", taskId)
: nls.localize('taskNotTracked', "The task '{0}' cannot be tracked. Make sure to have a problem matcher defined.", JSON.stringify(taskId));
e({ severity: severity.Error, message: errorMessage });
}
}, 10000);

View file

@ -15,6 +15,7 @@ const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint';
const DEBUG_DATA_BREAKPOINTS_KEY = 'debug.databreakpoint';
const DEBUG_EXCEPTION_BREAKPOINTS_KEY = 'debug.exceptionbreakpoint';
const DEBUG_WATCH_EXPRESSIONS_KEY = 'debug.watchexpressions';
const DEBUG_UX_STATE_KEY = 'debug.uxstate';
export class DebugStorage {
constructor(
@ -23,6 +24,14 @@ export class DebugStorage {
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService
) { }
loadDebugUxState(): 'simple' | 'default' {
return this.storageService.get(DEBUG_UX_STATE_KEY, StorageScope.WORKSPACE, 'simple') as 'simple' | 'default';
}
storeDebugUxState(value: 'simple' | 'default'): void {
this.storageService.store(DEBUG_UX_STATE_KEY, value, StorageScope.WORKSPACE, StorageTarget.USER);
}
loadBreakpoints(): Breakpoint[] {
let result: Breakpoint[] | undefined;
try {

View file

@ -13,7 +13,7 @@ import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/commo
import { CodeCellRenderTemplate, ICellOutputViewModel, IDisplayOutputViewModel, IInsetRenderOutput, INotebookEditor, IRenderOutput, outputHasDynamicHeight, RenderOutputType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/cellWidgets';
import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel';
import { BUILTIN_RENDERER_ID, CellUri, CellOutputKind, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { BUILTIN_RENDERER_ID, CellUri, CellOutputKind, NotebookCellOutputsSplice, IOrderedMimeType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
@ -69,45 +69,20 @@ export class CellOutputElement extends Disposable {
render(index: number, beforeElement?: HTMLElement) {
if (this.viewCell.metadata.outputCollapsed) {
return;
}
if (!this.notebookEditor.hasModel()) {
if (this.viewCell.metadata.outputCollapsed || !this.notebookEditor.hasModel()) {
return;
}
const notebookTextModel = this.notebookEditor.viewModel.notebookDocument;
let outputItemDiv;
let result: IRenderOutput | undefined = undefined;
let renderResult: IRenderOutput | undefined = undefined;
if (this.output.isDisplayOutput()) {
outputItemDiv = document.createElement('div');
const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel);
if (mimeTypes.length > 1) {
outputItemDiv.style.position = 'relative';
const mimeTypePicker = DOM.$('.multi-mimetype-output');
mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon));
mimeTypePicker.tabIndex = 0;
mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', '));
outputItemDiv.appendChild(mimeTypePicker);
this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => {
if (e.leftButton) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
}
}));
this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => {
const event = new StandardKeyboardEvent(e);
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
}
})));
this.attachMimetypeSwitcher(outputItemDiv, notebookTextModel, mimeTypes);
}
if (mimeTypes.length !== 0) {
@ -119,46 +94,44 @@ export class CellOutputElement extends Disposable {
if (pickedMimeTypeRenderer.rendererId !== BUILTIN_RENDERER_ID) {
const renderer = this.notebookService.getRendererInfo(pickedMimeTypeRenderer.rendererId);
result = renderer
renderResult = renderer
? { type: RenderOutputType.Extension, renderer, source: this.output, mimeType: pickedMimeTypeRenderer.mimeType }
: this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
} else {
result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, pickedMimeTypeRenderer.mimeType, this.getNotebookUri(),);
}
this.output.pickedMimeType = pick;
}
} else if (this.output.isStreamOutput()) {
if (!beforeElement && this.outputContainer.lastChild && (<HTMLElement>this.outputContainer.lastChild).classList.contains('stream-output') && this.output.isStreamOutput()) {
let innerContainer: HTMLElement;
if (!beforeElement && this.isPreviousDivStreamOutput() && this.output.isStreamOutput()) {
this.useDedicatedDOM = false;
// the previous output and this one are both stream output
outputItemDiv = this.outputContainer.lastChild as HTMLElement;
const innerContainer = outputItemDiv.lastChild && (<HTMLElement>outputItemDiv.lastChild).classList.contains('output-inner-container') ? outputItemDiv.lastChild as HTMLElement : document.createElement('div');
outputItemDiv.classList.add('stream-output');
DOM.append(outputItemDiv, innerContainer);
result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
innerContainer = outputItemDiv.lastChild && (<HTMLElement>outputItemDiv.lastChild).classList.contains('output-inner-container') ? outputItemDiv.lastChild as HTMLElement : document.createElement('div');
} else {
outputItemDiv = document.createElement('div');
const innerContainer = DOM.$('.output-inner-container');
outputItemDiv.classList.add('stream-output');
DOM.append(outputItemDiv, innerContainer);
result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
innerContainer = DOM.$('.output-inner-container');
}
outputItemDiv.classList.add('stream-output');
DOM.append(outputItemDiv, innerContainer);
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
} else {
// for text and error, there is no mimetype
outputItemDiv = document.createElement('div');
const innerContainer = DOM.$('.output-inner-container');
DOM.append(outputItemDiv, innerContainer);
result = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
renderResult = this.notebookEditor.getOutputRenderer().render(this.output, innerContainer, undefined, this.getNotebookUri(),);
}
this.domNode = outputItemDiv;
this.renderResult = result;
this.renderResult = renderResult;
if (!result) {
if (!renderResult) {
this.viewCell.updateOutputHeight(index, 0);
return;
}
@ -169,17 +142,14 @@ export class CellOutputElement extends Disposable {
this.outputContainer.appendChild(outputItemDiv);
}
if (result.type !== RenderOutputType.None) {
this.viewCell.selfSizeMonitoring = true;
this.notebookEditor.createInset(this.viewCell, result, this.viewCell.getOutputOffset(index));
if (renderResult.type !== RenderOutputType.None) {
this.notebookEditor.createInset(this.viewCell, renderResult, this.viewCell.getOutputOffset(index));
} else {
outputItemDiv.classList.add('foreground', 'output-element');
outputItemDiv.style.position = 'absolute';
}
if (outputHasDynamicHeight(result)) {
this.viewCell.selfSizeMonitoring = true;
if (outputHasDynamicHeight(renderResult)) {
const clientHeight = outputItemDiv.clientHeight;
const dimension = {
width: this.viewCell.layoutInfo.editorWidth,
@ -205,7 +175,7 @@ export class CellOutputElement extends Disposable {
elementSizeObserver.startObserving();
this.resizeListener.add(elementSizeObserver);
this.viewCell.updateOutputHeight(index, clientHeight);
} else if (result.type === RenderOutputType.None) { // no-op if it's a webview
} else if (renderResult.type === RenderOutputType.None) { // no-op if it's a webview
if (this.useDedicatedDOM) {
const clientHeight = Math.ceil(outputItemDiv.clientHeight);
this.viewCell.updateOutputHeight(index, clientHeight);
@ -216,6 +186,35 @@ export class CellOutputElement extends Disposable {
}
}
private isPreviousDivStreamOutput() {
return this.outputContainer.lastChild && (<HTMLElement>this.outputContainer.lastChild).classList.contains('stream-output');
}
private async attachMimetypeSwitcher(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, mimeTypes: readonly IOrderedMimeType[]) {
outputItemDiv.style.position = 'relative';
const mimeTypePicker = DOM.$('.multi-mimetype-output');
mimeTypePicker.classList.add(...ThemeIcon.asClassNameArray(mimetypeIcon));
mimeTypePicker.tabIndex = 0;
mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype, available mimetypes: {0}", mimeTypes.map(mimeType => mimeType.mimeType).join(', '));
outputItemDiv.appendChild(mimeTypePicker);
this.resizeListener.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => {
if (e.leftButton) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
}
}));
this.resizeListener.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => {
const event = new StandardKeyboardEvent(e);
if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) {
e.preventDefault();
e.stopPropagation();
await this.pickActiveMimeTypeRenderer(notebookTextModel, this.output as IDisplayOutputViewModel);
}
})));
}
private async pickActiveMimeTypeRenderer(notebookTextModel: NotebookTextModel, viewModel: IDisplayOutputViewModel) {
const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel);
@ -326,8 +325,6 @@ export class CellOutputContainer extends Disposable {
}
this.templateData.outputContainer.style.display = 'block';
// there are outputs, we need to calcualte their sizes and trigger relayout
// @TODO@rebornix, if there is no resizable output, we should not check their height individually, which hurts the performance
const outputsToRender = this._calcuateOutputsToRender();
for (let index = 0; index < outputsToRender.length; index++) {
const currOutput = this.viewCell.outputsViewModels[index];

View file

@ -23,14 +23,6 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod
protected readonly _onDidChangeOutputs = new Emitter<NotebookCellOutputsSplice[]>();
readonly onDidChangeOutputs = this._onDidChangeOutputs.event;
private _outputCollection: number[] = [];
private _selfSizeMonitoring: boolean = false;
set selfSizeMonitoring(newVal: boolean) {
this._selfSizeMonitoring = newVal;
}
get selfSizeMonitoring() {
return this._selfSizeMonitoring;
}
private _outputsTop: PrefixSumComputer | null = null;

View file

@ -485,10 +485,16 @@ export class NotebookTextModel extends Disposable implements INotebookTextModel
}
readonly label = 'Update Notebook Metadata';
undo() {
that._updateNotebookMetadata(oldMetadata, false);
that._updateNotebookMetadata({
...oldMetadata,
runState: that.metadata.runState
}, false);
}
redo() {
that._updateNotebookMetadata(metadata, false);
that._updateNotebookMetadata({
...metadata,
runState: that.metadata.runState
}, false);
}
}(), undefined, undefined);
}

View file

@ -91,7 +91,8 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel {
remotePort: 0,
description: '',
wideDescription: '',
icon: undefined
icon: undefined,
tooltip: ''
};
}
@ -264,7 +265,7 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer<ITunnelGrou
const isWide = templateData.container.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.parentElement?.classList.contains('wide');
const description = isWide ? node.wideDescription : node.description;
const label = node.label + (description ? (' - ' + description) : '');
templateData.iconLabel.setLabel(node.label, description, { title: label, extraClasses: ['tunnel-view-label'] });
templateData.iconLabel.setLabel(node.label, description, { title: node instanceof TunnelItem ? node.tooltip : label, extraClasses: ['tunnel-view-label'] });
templateData.actionBar.context = node;
const contextKeyService = this._register(this.contextKeyService.createScoped());
@ -517,6 +518,27 @@ class TunnelItem implements ITunnelItem {
}
}
get tooltip(): string {
let information: string = this.name ? nls.localize('remote.tunnel.tooltipName', "Port labeled {0}. ", this.name) : '';
if (this.localAddress) {
information += nls.localize('remote.tunnel.tooltipForwarded', "Remote port {0}:{1} forwarded to local address {2}. ", this.remoteHost, this.remotePort, this.localAddress);
} else {
information += nls.localize('remote.tunnel.tooltipCandidate', "Remote port {0}:{1} not forwarded. ", this.remoteHost, this.remotePort);
}
if (this.privacy === TunnelPrivacy.Public) {
information += nls.localize('remote.tunnel.tooltipPublic', "Accessible publicly. ");
} else {
information += nls.localize('remote.tunnel.tooltipPrivate', "Only accessible from this machine. ");
}
if (this.runningProcess) {
information += nls.localize('remote.tunnel.tooltipProcess', "Process: {0} ({1})", this.runningProcess.replace(/\0/g, ' ').trim(), this.pid);
}
return information;
}
}
export const TunnelTypeContextKey = new RawContextKey<TunnelType>('tunnelType', TunnelType.Add);
@ -615,11 +637,7 @@ export class TunnelPanel extends ViewPane {
accessibilityProvider: {
getAriaLabel: (item: ITunnelItem | ITunnelGroup) => {
if (item instanceof TunnelItem) {
if (item.localAddress) {
return nls.localize('remote.tunnel.ariaLabelForwarded', "Remote port {0}:{1} forwarded to local address {2}", item.remoteHost, item.remotePort, item.localAddress);
} else {
return nls.localize('remote.tunnel.ariaLabelCandidate', "Remote port {0}:{1} not forwarded", item.remoteHost, item.remotePort);
}
return item.tooltip;
} else {
return item.label;
}

View file

@ -16,6 +16,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EditorResourceAccessor } from 'vs/workbench/common/editor';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { stripIcons } from 'vs/base/common/iconLabels';
function getCount(repository: ISCMRepository): number {
if (typeof repository.provider.count === 'number') {
@ -147,11 +148,14 @@ export class SCMStatusController implements IWorkbenchContribution {
const disposables = new DisposableStore();
for (const command of commands) {
const tooltip = `${label} - ${command.tooltip}`;
const tooltip = `${label}${command.tooltip ? ` - ${command.tooltip}` : ''}`;
let ariaLabel = stripIcons(command.title).trim();
ariaLabel = ariaLabel ? `${ariaLabel}, ${label}` : label;
disposables.add(this.statusbarService.addEntry({
text: command.title,
ariaLabel: command.tooltip || label,
ariaLabel: `${ariaLabel}${command.tooltip ? ` - ${command.tooltip}` : ''}`,
tooltip,
command: command.id ? command : undefined
}, 'status.scm', localize('status.scm', "Source Control"), MainThreadStatusBarAlignment.LEFT, 10000));

View file

@ -182,24 +182,23 @@ export class TaskQuickPick extends Disposable {
picker.onDidTriggerItemButton(async (context) => {
let task = context.item.task;
if (task && !Types.isString(task) && context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) {
const key = task.getRecentlyUsedKey();
if (context.button.iconClass === ThemeIcon.asClassName(removeTaskIcon)) {
const key = (task && !Types.isString(task)) ? task.getRecentlyUsedKey() : undefined;
if (key) {
this.taskService.removeRecentlyUsedTask(key);
const indexToRemove = picker.items.indexOf(context.item);
if (indexToRemove >= 0) {
picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)];
}
}
return;
}
this.quickInputService.cancel();
if (ContributedTask.is(task)) {
this.taskService.customize(task, undefined, true);
} else if (CustomTask.is(task) || ConfiguringTask.is(task)) {
if (!(await this.taskService.openConfig(task))) {
const indexToRemove = picker.items.indexOf(context.item);
if (indexToRemove >= 0) {
picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)];
}
} else {
this.quickInputService.cancel();
if (ContributedTask.is(task)) {
this.taskService.customize(task, undefined, true);
} else if (CustomTask.is(task) || ConfiguringTask.is(task)) {
if (!(await this.taskService.openConfig(task))) {
this.taskService.customize(task, undefined, true);
}
}
}
});

View file

@ -34,6 +34,7 @@ import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/comm
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { selectBorder } from 'vs/platform/theme/common/colorRegistry';
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
import { equals } from 'vs/base/common/arrays';
const FIND_FOCUS_CLASS = 'find-focused';
@ -352,6 +353,8 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
class SwitchTerminalActionViewItem extends SelectActionViewItem {
private _lastOptions: ISelectOptionItem[] = [];
constructor(
action: IAction,
@ITerminalService private readonly _terminalService: ITerminalService,
@ -378,7 +381,12 @@ class SwitchTerminalActionViewItem extends SelectActionViewItem {
}
private _updateItems(): void {
this.setOptions(getTerminalSelectOpenItems(this._terminalService, this._contributions), this._terminalService.activeTabIndex);
const options = getTerminalSelectOpenItems(this._terminalService, this._contributions);
// only update options if they've changed
if (!equals(Object.values(options), Object.values(this._lastOptions))) {
this.setOptions(options, this._terminalService.activeTabIndex);
this._lastOptions = options;
}
}
}

View file

@ -16,14 +16,14 @@ suite('Workbench - TerminalConfigHelper', () => {
fixture = document.body;
});
// test('TerminalConfigHelper - getFont fontFamily', async () => {
// const configurationService = new TestConfigurationService();
// await configurationService.setUserConfiguration('editor', { fontFamily: 'foo' });
// await configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } });
// const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!);
// configHelper.panelContainer = fixture;
// assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily');
// });
test('TerminalConfigHelper - getFont fontFamily', async () => {
const configurationService = new TestConfigurationService();
await configurationService.setUserConfiguration('editor', { fontFamily: 'foo' });
await configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: 'bar' } });
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontFamily, 'bar', 'terminal.integrated.fontFamily should be selected over editor.fontFamily');
});
test('TerminalConfigHelper - getFont fontFamily (Linux Fedora)', async () => {
const configurationService = new TestConfigurationService();

View file

@ -116,10 +116,6 @@
flex: 150px 1 1000
}
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-media {
min-height: 300px;
}
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-category .codicon {
margin-left:0;
font-size: 22pt;
@ -222,6 +218,10 @@
max-width: 800px;
}
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer .gettingStartedSlide.detail .getting-started-detail-right img {
object-fit: contain;
}
.monaco-workbench .part.editor > .content .walkThroughContent .gettingStartedContainer button {
border: none;
color: inherit;

View file

@ -16,7 +16,7 @@ import { $, addDisposableListener } from 'vs/base/browser/dom';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IProductService } from 'vs/platform/product/common/productService';
import { IGettingStartedCategoryWithProgress, IGettingStartedService } from 'vs/workbench/services/gettingStarted/common/gettingStartedService';
import { registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { welcomePageBackground, welcomePageProgressBackground, welcomePageProgressForeground, welcomePageTileBackground, welcomePageTileHoverBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePageColors';
import { activeContrastBorder, buttonBackground, buttonForeground, buttonHoverBackground, buttonSecondaryBackground, contrastBorder, descriptionForeground, focusBorder, foreground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@ -26,6 +26,7 @@ import { gettingStartedCheckedCodicon, gettingStartedUncheckedCodicon } from 'vs
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { URI } from 'vs/base/common/uri';
export const gettingStartedInputTypeId = 'workbench.editors.gettingStartedInput';
const telemetryFrom = 'gettingStartedPage';
@ -98,6 +99,7 @@ export class GettingStartedPage extends Disposable {
@IGettingStartedService private readonly gettingStartedService: IGettingStartedService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IOpenerService private readonly openerService: IOpenerService,
@IThemeService private readonly themeService: IThemeService,
) {
super();
@ -141,6 +143,8 @@ export class GettingStartedPage extends Disposable {
if (command) {
this.dispatchListeners.add(addDisposableListener(element, 'click', (e) => {
this.commandService.executeCommand('workbench.action.keepEditor');
type GettingStartedActionClassification = {
command: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
argument: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
@ -202,7 +206,7 @@ export class GettingStartedPage extends Disposable {
}
private selectTask(container: HTMLElement, id: string | undefined, contractIfAlreadySelected = true) {
const mediaElement = assertIsDefined(container.querySelector('.getting-started-media'));
const mediaElement = assertIsDefined(container.querySelector('.getting-started-media') as HTMLImageElement);
this.taskDisposables.clear();
if (id) {
const taskElement = assertIsDefined(container.querySelector(`[data-task-id="${id}"]`));
@ -218,9 +222,11 @@ export class GettingStartedPage extends Disposable {
this.editorInput.selectedTask = id;
const taskToExpand = assertIsDefined(this.currentCategory.content.items.find(task => task.id === id));
mediaElement.setAttribute('src', taskToExpand.media.path.toString());
mediaElement.setAttribute('alt', taskToExpand.media.altText);
this.updateMediaSourceForColorMode(mediaElement, taskToExpand.media.path);
this.taskDisposables.add(addDisposableListener(mediaElement, 'load', () => mediaElement.width = mediaElement.naturalWidth * 2 / 3));
this.taskDisposables.add(addDisposableListener(mediaElement, 'click', () => taskElement.querySelector('button')?.click()));
this.taskDisposables.add(this.themeService.onDidColorThemeChange(() => this.updateMediaSourceForColorMode(mediaElement, taskToExpand.media.path)));
taskElement.classList.add('expanded');
} else {
this.editorInput.selectedTask = undefined;
@ -231,6 +237,11 @@ export class GettingStartedPage extends Disposable {
this.detailImageScrollbar?.scanDomNode();
}
private updateMediaSourceForColorMode(element: HTMLImageElement, sources: { hc: URI, dark: URI, light: URI }) {
const themeType = this.themeService.getColorTheme().type;
element.src = sources[themeType].toString();
}
onReady(container: HTMLElement) {
const categoryElements = this.gettingStartedCategories.map(
category => {

View file

@ -6,7 +6,7 @@
/* eslint-disable code-no-standalone-editor */
/* eslint-disable code-import-patterns */
import { ConsoleLogService } from 'vs/platform/log/common/log';
import { ConsoleLogger, LogService } from 'vs/platform/log/common/log';
import { ISignService } from 'vs/platform/sign/common/sign';
import { URI } from 'vs/base/common/uri';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
@ -197,7 +197,13 @@ export class SimpleConfigurationService extends BaseSimpleConfigurationService i
//#region Logger
export class SimpleLogService extends ConsoleLogService { }
export class SimpleLogService extends LogService {
constructor() {
super(new ConsoleLogger());
}
}
export class SimpleSignService implements ISignService {

View file

@ -415,7 +415,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku
.then(extensionPaths => {
const node_module = <any>require.__$__nodeRequire('module');
const original = node_module._load;
node_module._load = function load(request: string, parent: any, isMain: any) {
node_module._load = function load(request: string, parent: { filename: string; }, isMain: boolean) {
if (request === 'tls') {
return lookup.tls;
}

View file

@ -23,7 +23,7 @@ type GettingStartedItem = {
| { title: string, command: string, link?: never },
doneOn: { commandExecuted: string, eventFired?: never } | { eventFired: string, commandExecuted?: never, }
when?: string,
media: { type: 'image', path: string, altText: string },
media: { type: 'image', path: string | { hc: string, light: string, dark: string }, altText: string },
};
type GettingStartedCategory = {
@ -228,7 +228,13 @@ export const content: GettingStartedContent = [
command: 'workbench.action.openSettings'
},
doneOn: { commandExecuted: 'workbench.action.openSettings' },
media: { type: 'image', altText: 'VS Code Settings', path: 'settings.png' },
media: {
type: 'image', altText: 'VS Code Settings', path: {
dark: 'dark/settings.png',
light: 'light/settings.png',
hc: 'hc/settings.png',
}
},
},
{
id: 'videoTutorial',

View file

@ -28,7 +28,7 @@ export interface IGettingStartedTask {
| { title: string, command?: never, link: string }
| { title: string, command: string, link?: never },
doneOn: { commandExecuted: string, eventFired?: never } | { eventFired: string, commandExecuted?: never, }
media: { type: 'image', path: URI, altText: string },
media: { type: 'image', path: { hc: URI, light: URI, dark: URI }, altText: string },
}
export interface IGettingStartedCategoryDescriptor {
@ -131,6 +131,22 @@ content.forEach(category => {
});
if (category.content.type === 'items') {
const convertPaths = (path: string | { hc: string, dark: string, light: string }): { hc: URI, dark: URI, light: URI } => {
const convertPath = (path: string) => path.startsWith('https://')
? URI.parse(path, true)
: FileAccess.asFileUri('vs/workbench/services/gettingStarted/common/media/' + path, require);
if (typeof path === 'string') {
const converted = convertPath(path);
return { hc: converted, dark: converted, light: converted };
} else {
return {
hc: convertPath(path.hc),
light: convertPath(path.light),
dark: convertPath(path.dark)
};
}
};
category.content.items.forEach((item, index) => {
registryImpl.registerTask({
...item,
@ -140,9 +156,7 @@ content.forEach(category => {
media: {
type: item.media.type,
altText: item.media.altText,
path: item.media.path.startsWith('https://')
? URI.parse(item.media.path, true)
: FileAccess.asFileUri('vs/workbench/services/gettingStarted/common/media/' + item.media.path, require)
path: convertPaths(item.media.path)
}
});
});

Some files were not shown because too many files have changed in this diff Show more