mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-29 09:43:49 +01:00
Merge remote-tracking branch 'origin/master' into pr/baileyherbert/90631
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ms-vscode.js-debug-nightly",
|
||||
"version": "2020.2.1117",
|
||||
"version": "2020.2.1317",
|
||||
"forQualities": [
|
||||
"insider"
|
||||
],
|
||||
|
||||
@@ -447,6 +447,22 @@
|
||||
"command": "git.stashDrop",
|
||||
"title": "%command.stashDrop%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"title": "%command.timelineOpenDiff%",
|
||||
"icon": "$(compare-changes)",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitId",
|
||||
"title": "%command.timelineCopyCommitId%",
|
||||
"category": "Git"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitMessage",
|
||||
"title": "%command.timelineCopyCommitMessage%",
|
||||
"category": "Git"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -718,14 +734,21 @@
|
||||
{
|
||||
"command": "git.stashDrop",
|
||||
"when": "config.git.enabled && gitOpenRepositoryCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitId",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitMessage",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"scm/title": [
|
||||
{
|
||||
"command": "git.init",
|
||||
"group": "navigation",
|
||||
"when": "config.git.enabled && !scmProvider && gitOpenRepositoryCount == 0 && workspaceFolderCount != 0"
|
||||
},
|
||||
{
|
||||
"command": "git.commit",
|
||||
"group": "navigation",
|
||||
@@ -1248,6 +1271,28 @@
|
||||
"command": "git.revertChange",
|
||||
"when": "originalResourceScheme == git"
|
||||
}
|
||||
],
|
||||
"timeline/item/context": [
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "inline",
|
||||
"when": "timelineItem =~ /git:file\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.openDiff",
|
||||
"group": "1_timeline",
|
||||
"when": "timelineItem =~ /git:file\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitId",
|
||||
"group": "2_timeline@1",
|
||||
"when": "timelineItem =~ /git:file:commit\\b/"
|
||||
},
|
||||
{
|
||||
"command": "git.timeline.copyCommitMessage",
|
||||
"group": "2_timeline@2",
|
||||
"when": "timelineItem =~ /git:file:commit\\b/"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
@@ -1761,7 +1806,34 @@
|
||||
72
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewsWelcome": [
|
||||
{
|
||||
"view": "workbench.scm",
|
||||
"contents": "%view.workbench.scm.disabled%",
|
||||
"when": "!config.git.enabled"
|
||||
},
|
||||
{
|
||||
"view": "workbench.scm",
|
||||
"contents": "%view.workbench.scm.missing%",
|
||||
"when": "config.git.enabled && git.missing"
|
||||
},
|
||||
{
|
||||
"view": "workbench.scm",
|
||||
"contents": "%view.workbench.scm.empty%",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == empty"
|
||||
},
|
||||
{
|
||||
"view": "workbench.scm",
|
||||
"contents": "%view.workbench.scm.folder%",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == folder"
|
||||
},
|
||||
{
|
||||
"view": "workbench.scm",
|
||||
"contents": "%view.workbench.scm.workspace%",
|
||||
"when": "config.git.enabled && !git.missing && workbenchState == workspace"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"byline": "^5.0.0",
|
||||
|
||||
@@ -70,6 +70,9 @@
|
||||
"command.stashApply": "Apply Stash...",
|
||||
"command.stashApplyLatest": "Apply Latest Stash",
|
||||
"command.stashDrop": "Drop Stash...",
|
||||
"command.timelineOpenDiff": "Open Changes",
|
||||
"command.timelineCopyCommitId": "Copy Commit ID",
|
||||
"command.timelineCopyCommitMessage": "Copy Commit Message",
|
||||
"config.enabled": "Whether git is enabled.",
|
||||
"config.path": "Path and filename of the git executable, e.g. `C:\\Program Files\\Git\\bin\\git.exe` (Windows).",
|
||||
"config.autoRepositoryDetection": "Configures when repositories should be automatically detected.",
|
||||
@@ -127,7 +130,7 @@
|
||||
"config.showProgress": "Controls whether git actions should show progress.",
|
||||
"config.rebaseWhenSync": "Force git to use rebase when running the sync command.",
|
||||
"config.confirmEmptyCommits": "Always confirm the creation of empty commits for the 'Git: Commit Empty' command.",
|
||||
"config.fetchOnPull": "Fetch all branches when pulling or just the current one.",
|
||||
"config.fetchOnPull": "When enabled, fetch all branches when pulling. Otherwise, fetch just the current one.",
|
||||
"config.pullTags": "Fetch all tags when pulling.",
|
||||
"config.autoStash": "Stash any changes before pulling and restore them after successful pull.",
|
||||
"config.allowForcePush": "Controls whether force push (with or without lease) is enabled.",
|
||||
@@ -146,5 +149,10 @@
|
||||
"colors.untracked": "Color for untracked resources.",
|
||||
"colors.ignored": "Color for ignored resources.",
|
||||
"colors.conflict": "Color for resources with conflicts.",
|
||||
"colors.submodule": "Color for submodule resources."
|
||||
"colors.submodule": "Color for submodule resources.",
|
||||
"view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use Git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.",
|
||||
"view.workbench.scm.disabled": "If you would like to use git features, please enable git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.empty": "In order to use git features, you can open a folder containing a git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone from URL](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.folder": "The folder currently open doesn't have a git repository.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).",
|
||||
"view.workbench.scm.workspace": "The workspace currently open doesn't have any folders containing git repositories.\n[Initialize Repository](command:git.init)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm)."
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { lstat, Stats } from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder } from 'vscode';
|
||||
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode';
|
||||
import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git';
|
||||
@@ -17,6 +17,7 @@ import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineC
|
||||
import { fromGitUri, toGitUri, isGitUri } from './uri';
|
||||
import { grep, isDescendant, pathEquals } from './util';
|
||||
import { Log, LogLevel } from './log';
|
||||
import { GitTimelineItem } from './timelineProvider';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -2331,23 +2332,47 @@ export class CommandCenter {
|
||||
return result && result.stash;
|
||||
}
|
||||
|
||||
@command('git.openDiff', { repository: false })
|
||||
async openDiff(uri: Uri, lhs: string, rhs: string) {
|
||||
@command('git.timeline.openDiff', { repository: false })
|
||||
async timelineOpenDiff(item: TimelineItem, uri: Uri | undefined, _source: string) {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (uri == null || !GitTimelineItem.is(item)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const basename = path.basename(uri.fsPath);
|
||||
|
||||
let title;
|
||||
if ((lhs === 'HEAD' || lhs === '~') && rhs === '') {
|
||||
if ((item.previousRef === 'HEAD' || item.previousRef === '~') && item.ref === '') {
|
||||
title = `${basename} (Working Tree)`;
|
||||
}
|
||||
else if (lhs === 'HEAD' && rhs === '~') {
|
||||
else if (item.previousRef === 'HEAD' && item.ref === '~') {
|
||||
title = `${basename} (Index)`;
|
||||
} else {
|
||||
title = `${basename} (${lhs.endsWith('^') ? `${lhs.substr(0, 8)}^` : lhs.substr(0, 8)}) \u27f7 ${basename} (${rhs.endsWith('^') ? `${rhs.substr(0, 8)}^` : rhs.substr(0, 8)})`;
|
||||
title = `${basename} (${item.shortPreviousRef}) \u27f7 ${basename} (${item.shortRef})`;
|
||||
}
|
||||
|
||||
return commands.executeCommand('vscode.diff', toGitUri(uri, lhs), rhs === '' ? uri : toGitUri(uri, rhs), title);
|
||||
return commands.executeCommand('vscode.diff', toGitUri(uri, item.previousRef), item.ref === '' ? uri : toGitUri(uri, item.ref), title);
|
||||
}
|
||||
|
||||
@command('git.timeline.copyCommitId', { repository: false })
|
||||
async timelineCopyCommitId(item: TimelineItem, _uri: Uri | undefined, _source: string) {
|
||||
if (!GitTimelineItem.is(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
env.clipboard.writeText(item.ref);
|
||||
}
|
||||
|
||||
@command('git.timeline.copyCommitMessage', { repository: false })
|
||||
async timelineCopyCommitMessage(item: TimelineItem, _uri: Uri | undefined, _source: string) {
|
||||
if (!GitTimelineItem.is(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
env.clipboard.writeText(item.message);
|
||||
}
|
||||
|
||||
|
||||
private createCommand(id: string, key: string, method: Function, options: CommandOptions): (...args: any[]) => any {
|
||||
const result = (...args: any[]) => {
|
||||
let result: Promise<any>;
|
||||
|
||||
@@ -175,6 +175,7 @@ export async function activate(context: ExtensionContext): Promise<GitExtension>
|
||||
console.warn(err.message);
|
||||
outputChannel.appendLine(err.message);
|
||||
|
||||
commands.executeCommand('setContext', 'git.missing', true);
|
||||
warnAboutMissingGit();
|
||||
|
||||
return new GitExtensionImpl();
|
||||
|
||||
@@ -5,19 +5,62 @@
|
||||
|
||||
import * as dayjs from 'dayjs';
|
||||
import * as advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import * as relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, TimelineItem, TimelineProvider, Uri, workspace, TimelineChangeEvent } from 'vscode';
|
||||
import { CancellationToken, Disposable, Event, EventEmitter, ThemeIcon, Timeline, TimelineChangeEvent, TimelineCursor, TimelineItem, TimelineProvider, Uri, workspace } from 'vscode';
|
||||
import { Model } from './model';
|
||||
import { Repository } from './repository';
|
||||
import { debounce } from './decorators';
|
||||
import { Status } from './api/git';
|
||||
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
// TODO[ECA]: Localize all the strings
|
||||
// TODO[ECA]: Localize or use a setting for date format
|
||||
|
||||
export class GitTimelineItem extends TimelineItem {
|
||||
static is(item: TimelineItem): item is GitTimelineItem {
|
||||
return item instanceof GitTimelineItem;
|
||||
}
|
||||
|
||||
readonly ref: string;
|
||||
readonly previousRef: string;
|
||||
readonly message: string;
|
||||
|
||||
constructor(
|
||||
ref: string,
|
||||
previousRef: string,
|
||||
message: string,
|
||||
timestamp: number,
|
||||
id: string,
|
||||
contextValue: string
|
||||
) {
|
||||
const index = message.indexOf('\n');
|
||||
const label = index !== -1 ? `${message.substring(0, index)} \u2026` : message;
|
||||
|
||||
super(label, timestamp);
|
||||
|
||||
this.ref = ref;
|
||||
this.previousRef = previousRef;
|
||||
this.message = message;
|
||||
this.id = id;
|
||||
this.contextValue = contextValue;
|
||||
}
|
||||
|
||||
get shortRef() {
|
||||
return this.shortenRef(this.ref);
|
||||
}
|
||||
|
||||
get shortPreviousRef() {
|
||||
return this.shortenRef(this.previousRef);
|
||||
}
|
||||
|
||||
private shortenRef(ref: string): string {
|
||||
if (ref === '' || ref === '~' || ref === 'HEAD') {
|
||||
return ref;
|
||||
}
|
||||
return ref.endsWith('^') ? `${ref.substr(0, 8)}^` : ref.substr(0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
export class GitTimelineProvider implements TimelineProvider {
|
||||
private _onDidChange = new EventEmitter<TimelineChangeEvent>();
|
||||
get onDidChange(): Event<TimelineChangeEvent> {
|
||||
@@ -44,7 +87,7 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
this._disposable.dispose();
|
||||
}
|
||||
|
||||
async provideTimeline(uri: Uri, _token: CancellationToken): Promise<TimelineItem[]> {
|
||||
async provideTimeline(uri: Uri, _cursor: TimelineCursor, _token: CancellationToken): Promise<Timeline> {
|
||||
// console.log(`GitTimelineProvider.provideTimeline: uri=${uri} state=${this._model.state}`);
|
||||
|
||||
const repo = this._model.getRepository(uri);
|
||||
@@ -53,7 +96,7 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
this._repoStatusDate = undefined;
|
||||
this._repo = undefined;
|
||||
|
||||
return [];
|
||||
return { items: [] };
|
||||
}
|
||||
|
||||
if (this._repo?.root !== repo.root) {
|
||||
@@ -72,25 +115,17 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
const commits = await repo.logFile(uri);
|
||||
|
||||
let dateFormatter: dayjs.Dayjs;
|
||||
const items = commits.map<TimelineItem>(c => {
|
||||
let message = c.message;
|
||||
|
||||
const index = message.indexOf('\n');
|
||||
if (index !== -1) {
|
||||
message = `${message.substring(0, index)} \u2026`;
|
||||
}
|
||||
|
||||
const items = commits.map<GitTimelineItem>(c => {
|
||||
dateFormatter = dayjs(c.authorDate);
|
||||
|
||||
const item = new TimelineItem(message, c.authorDate?.getTime() ?? 0);
|
||||
item.id = c.hash;
|
||||
const item = new GitTimelineItem(c.hash, `${c.hash}^`, c.message, c.authorDate?.getTime() ?? 0, c.hash, 'git:file:commit');
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
item.description = `${dateFormatter.fromNow()} \u2022 ${c.authorName}`;
|
||||
item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n\n${c.message}`;
|
||||
item.description = c.authorName;
|
||||
item.detail = `${c.authorName} (${c.authorEmail}) \u2014 ${c.hash.substr(0, 8)}\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n\n${c.message}`;
|
||||
item.command = {
|
||||
title: 'Open Diff',
|
||||
command: 'git.openDiff',
|
||||
arguments: [uri, `${c.hash}^`, c.hash]
|
||||
title: 'Open Comparison',
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [uri, this.id, item]
|
||||
};
|
||||
|
||||
return item;
|
||||
@@ -123,16 +158,15 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
break;
|
||||
}
|
||||
|
||||
const item = new TimelineItem('Staged Changes', date.getTime());
|
||||
item.id = 'index';
|
||||
const item = new GitTimelineItem('~', 'HEAD', 'Staged Changes', date.getTime(), 'index', 'git:file:index');
|
||||
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
item.description = `${dateFormatter.fromNow()} \u2022 You`;
|
||||
item.detail = `You \u2014 Index\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
|
||||
item.description = 'You';
|
||||
item.detail = `You \u2014 Index\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`;
|
||||
item.command = {
|
||||
title: 'Open Comparison',
|
||||
command: 'git.openDiff',
|
||||
arguments: [uri, 'HEAD', '~']
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [uri, this.id, item]
|
||||
};
|
||||
|
||||
items.push(item);
|
||||
@@ -166,22 +200,21 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
break;
|
||||
}
|
||||
|
||||
const item = new TimelineItem('Uncommited Changes', date.getTime());
|
||||
item.id = 'working';
|
||||
const item = new GitTimelineItem('', index ? '~' : 'HEAD', 'Uncommited Changes', date.getTime(), 'working', 'git:file:working');
|
||||
// TODO[ECA]: Replace with a better icon -- reflecting its status maybe?
|
||||
item.iconPath = new (ThemeIcon as any)('git-commit');
|
||||
item.description = `${dateFormatter.fromNow()} \u2022 You`;
|
||||
item.detail = `You \u2014 Working Tree\n${dateFormatter.fromNow()} (${dateFormatter.format('MMMM Do, YYYY h:mma')})\n${status}`;
|
||||
item.description = 'You';
|
||||
item.detail = `You \u2014 Working Tree\n${dateFormatter.format('MMMM Do, YYYY h:mma')}\n${status}`;
|
||||
item.command = {
|
||||
title: 'Open Comparison',
|
||||
command: 'git.openDiff',
|
||||
arguments: [uri, index ? '~' : 'HEAD', '']
|
||||
command: 'git.timeline.openDiff',
|
||||
arguments: [uri, this.id, item]
|
||||
};
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
return items;
|
||||
return { items: items };
|
||||
}
|
||||
|
||||
private onRepositoriesChanged(_repo: Repository) {
|
||||
@@ -208,6 +241,6 @@ export class GitTimelineProvider implements TimelineProvider {
|
||||
|
||||
@debounce(500)
|
||||
private fireChanged() {
|
||||
this._onDidChange.fire();
|
||||
this._onDidChange.fire({});
|
||||
}
|
||||
}
|
||||
|
||||
+14
-19
@@ -382,7 +382,7 @@ har-schema@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
||||
|
||||
har-validator@~5.1.0:
|
||||
har-validator@~5.1.3:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
|
||||
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
|
||||
@@ -697,17 +697,12 @@ performance-now@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
|
||||
|
||||
psl@^1.1.24:
|
||||
psl@^1.1.28:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
|
||||
integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
|
||||
|
||||
punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
|
||||
|
||||
punycode@^2.1.0:
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
@@ -723,9 +718,9 @@ querystringify@^2.1.1:
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
request@^2.88.0:
|
||||
version "2.88.0"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
|
||||
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
|
||||
version "2.88.2"
|
||||
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
|
||||
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
@@ -734,7 +729,7 @@ request@^2.88.0:
|
||||
extend "~3.0.2"
|
||||
forever-agent "~0.6.1"
|
||||
form-data "~2.3.2"
|
||||
har-validator "~5.1.0"
|
||||
har-validator "~5.1.3"
|
||||
http-signature "~1.2.0"
|
||||
is-typedarray "~1.0.0"
|
||||
isstream "~0.1.2"
|
||||
@@ -744,7 +739,7 @@ request@^2.88.0:
|
||||
performance-now "^2.1.0"
|
||||
qs "~6.5.2"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.4.3"
|
||||
tough-cookie "~2.5.0"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
@@ -822,13 +817,13 @@ supports-color@5.4.0:
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
tough-cookie@~2.4.3:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
|
||||
integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||
dependencies:
|
||||
psl "^1.1.24"
|
||||
punycode "^1.4.1"
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tunnel-agent@^0.6.0:
|
||||
version "0.6.0"
|
||||
|
||||
@@ -99,6 +99,14 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens
|
||||
case PConst.Kind.memberSetAccessor:
|
||||
case PConst.Kind.constructorImplementation:
|
||||
case PConst.Kind.memberVariable:
|
||||
// Don't show if child and parent have same start
|
||||
// For https://github.com/microsoft/vscode/issues/90396
|
||||
if (parent &&
|
||||
typeConverters.Position.fromLocation(parent.spans[0].start).isEqual(typeConverters.Position.fromLocation(item.spans[0].start))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Only show if parent is a class type object (not a literal)
|
||||
switch (parent?.kind) {
|
||||
case PConst.Kind.class:
|
||||
|
||||
@@ -91,6 +91,19 @@ suite('TypeScript References', () => {
|
||||
const codeLenses = await getCodeLenses(testDocumentUri);
|
||||
assert.strictEqual(codeLenses?.length, 0);
|
||||
});
|
||||
|
||||
test('Should not show duplicate references on ES5 class (https://github.com/microsoft/vscode/issues/90396)', async () => {
|
||||
const testDocumentUri = vscode.Uri.parse('untitled:test3.js');
|
||||
await createTestEditor(testDocumentUri,
|
||||
`function A() {`,
|
||||
` console.log("hi");`,
|
||||
`}`,
|
||||
`A.x = {};`,
|
||||
);
|
||||
|
||||
const codeLenses = await getCodeLenses(testDocumentUri);
|
||||
assert.strictEqual(codeLenses?.length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
function getCodeLenses(document: vscode.Uri): Thenable<readonly vscode.CodeLens[] | undefined> {
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
* 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';
|
||||
import 'mocha';
|
||||
import * as os from 'os';
|
||||
import { join } from 'path';
|
||||
import { closeAllEditors, disposeAll, conditionalTest } from '../utils';
|
||||
import * as vscode from 'vscode';
|
||||
import { closeAllEditors, conditionalTest, delay, disposeAll } from '../utils';
|
||||
|
||||
const webviewId = 'myWebview';
|
||||
|
||||
@@ -332,8 +333,30 @@ suite('Webview tests', () => {
|
||||
webview.webview.postMessage({ value: 1 });
|
||||
await firstResponse;
|
||||
assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One);
|
||||
|
||||
});
|
||||
|
||||
if (os.platform() === 'darwin') {
|
||||
conditionalTest('webview can copy text from webview', async () => {
|
||||
const expectedText = `webview text from: ${Date.now()}!`;
|
||||
|
||||
const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.One }, { enableScripts: true, retainContextWhenHidden: true }));
|
||||
const ready = getMesssage(webview);
|
||||
|
||||
|
||||
webview.webview.html = createHtmlDocumentWithBody(/*html*/`
|
||||
<b>${expectedText}</b>
|
||||
<script>
|
||||
const vscode = acquireVsCodeApi();
|
||||
document.execCommand('selectAll');
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
</script>`);
|
||||
await ready;
|
||||
|
||||
await vscode.commands.executeCommand('editor.action.webvieweditor.copy');
|
||||
await delay(200); // Make sure copy has time to reach webview
|
||||
assert.strictEqual(await vscode.env.clipboard.readText(), expectedText);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function createHtmlDocumentWithBody(body: string): string {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, env, UIKind } from 'vscode';
|
||||
import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind } from 'vscode';
|
||||
import { join } from 'path';
|
||||
import { closeAllEditors, pathEquals, createRandomFile } from '../utils';
|
||||
|
||||
@@ -146,12 +146,6 @@ suite('window namespace tests', () => {
|
||||
});
|
||||
|
||||
test('active editor not always correct... #49125', async function () {
|
||||
if (env.uiKind === UIKind.Web) {
|
||||
// https://github.com/microsoft/vscode/issues/90470
|
||||
// https://github.com/microsoft/playwright/issues/979
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
const randomFile1 = await createRandomFile();
|
||||
const randomFile2 = await createRandomFile();
|
||||
|
||||
|
||||
@@ -215,13 +215,6 @@ suite('workspace-namespace', () => {
|
||||
});
|
||||
|
||||
test('eol, change via onWillSave', async function () {
|
||||
if (vscode.env.uiKind === vscode.UIKind.Web) {
|
||||
// TODO@Jo Test seems to fail when running in web due to
|
||||
// onWillSaveTextDocument not getting called
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
let called = false;
|
||||
let sub = vscode.workspace.onWillSaveTextDocument(e => {
|
||||
called = true;
|
||||
@@ -245,26 +238,32 @@ suite('workspace-namespace', () => {
|
||||
sub.dispose();
|
||||
});
|
||||
|
||||
function assertEqualPath(a: string, b: string): void {
|
||||
assert.ok(pathEquals(a, b), `${a} <-> ${b}`);
|
||||
}
|
||||
|
||||
test('events: onDidOpenTextDocument, onDidChangeTextDocument, onDidSaveTextDocument', async () => {
|
||||
const file = await createRandomFile();
|
||||
let disposables: vscode.Disposable[] = [];
|
||||
|
||||
await vscode.workspace.saveAll();
|
||||
|
||||
let pendingAsserts: Function[] = [];
|
||||
let onDidOpenTextDocument = false;
|
||||
disposables.push(vscode.workspace.onDidOpenTextDocument(e => {
|
||||
pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath)));
|
||||
pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath));
|
||||
onDidOpenTextDocument = true;
|
||||
}));
|
||||
|
||||
let onDidChangeTextDocument = false;
|
||||
disposables.push(vscode.workspace.onDidChangeTextDocument(e => {
|
||||
pendingAsserts.push(() => assert.ok(pathEquals(e.document.uri.fsPath, file.fsPath)));
|
||||
pendingAsserts.push(() => assertEqualPath(e.document.uri.fsPath, file.fsPath));
|
||||
onDidChangeTextDocument = true;
|
||||
}));
|
||||
|
||||
let onDidSaveTextDocument = false;
|
||||
disposables.push(vscode.workspace.onDidSaveTextDocument(e => {
|
||||
pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath)));
|
||||
pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath));
|
||||
onDidSaveTextDocument = true;
|
||||
}));
|
||||
|
||||
@@ -291,7 +290,7 @@ suite('workspace-namespace', () => {
|
||||
|
||||
let onDidSaveTextDocument = false;
|
||||
disposables.push(vscode.workspace.onDidSaveTextDocument(e => {
|
||||
pendingAsserts.push(() => assert.ok(pathEquals(e.uri.fsPath, file.fsPath)));
|
||||
pendingAsserts.push(() => assertEqualPath(e.uri.fsPath, file.fsPath));
|
||||
onDidSaveTextDocument = true;
|
||||
}));
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.43.0",
|
||||
"distro": "a87981a5cc52fed24735d447e954adccb9c9f914",
|
||||
"distro": "821d61f0e38d2eb3e02bec3962fe61e4e5a61527",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
@@ -139,7 +139,7 @@
|
||||
"opn": "^6.0.0",
|
||||
"optimist": "0.3.5",
|
||||
"p-all": "^1.0.0",
|
||||
"playwright": "^0.10.0",
|
||||
"playwright": "0.11.0",
|
||||
"pump": "^1.0.1",
|
||||
"queue": "3.0.6",
|
||||
"rcedit": "^1.1.0",
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ function code() {
|
||||
export VSCODE_LOGS=
|
||||
|
||||
# Launch Code
|
||||
exec "$CODE" . "$@"
|
||||
exec "$CODE" . --no-sandbox "$@"
|
||||
}
|
||||
|
||||
function code-wsl()
|
||||
|
||||
+7
-1
@@ -82,7 +82,7 @@ app.once('ready', function () {
|
||||
traceOptions: args['trace-options'] || 'record-until-full,enable-sampling'
|
||||
};
|
||||
|
||||
contentTracing.startRecording(traceOptions, () => onReady());
|
||||
contentTracing.startRecording(traceOptions).finally(() => onReady());
|
||||
} else {
|
||||
onReady();
|
||||
}
|
||||
@@ -133,6 +133,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
|
||||
// provided by Electron
|
||||
'disable-color-correct-rendering'
|
||||
];
|
||||
if (process.platform === 'linux') {
|
||||
SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility');
|
||||
}
|
||||
|
||||
// Read argv config
|
||||
const argvConfig = readArgvConfigSync();
|
||||
@@ -159,6 +162,9 @@ function configureCommandlineSwitchesSync(cliArgs) {
|
||||
app.commandLine.appendSwitch('js-flags', jsFlags);
|
||||
}
|
||||
|
||||
// TODO@Ben TODO@Deepak Electron 7 workaround for https://github.com/microsoft/vscode/issues/88873
|
||||
app.commandLine.appendSwitch('disable-features', 'LayoutNG');
|
||||
|
||||
return argvConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
src: url("./codicon.ttf?6caeeccc06315e827f3bff83885456fb") format("truetype");
|
||||
src: url("./codicon.ttf?d0510f6ecacbb2788db2b3162273a3d8") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
|
||||
Binary file not shown.
@@ -5,6 +5,53 @@
|
||||
|
||||
import { pad } from './strings';
|
||||
|
||||
const minute = 60;
|
||||
const hour = minute * 60;
|
||||
const day = hour * 24;
|
||||
const week = day * 7;
|
||||
const month = day * 30;
|
||||
const year = day * 365;
|
||||
|
||||
// TODO[ECA]: Localize strings
|
||||
export function fromNow(date: number | Date) {
|
||||
if (typeof date !== 'number') {
|
||||
date = date.getTime();
|
||||
}
|
||||
|
||||
const seconds = Math.round((new Date().getTime() - date) / 1000);
|
||||
if (seconds < 30) {
|
||||
return 'now';
|
||||
}
|
||||
|
||||
let value: number;
|
||||
let unit: string;
|
||||
if (seconds < minute) {
|
||||
value = seconds;
|
||||
unit = 'sec';
|
||||
} else if (seconds < hour) {
|
||||
value = Math.floor(seconds / minute);
|
||||
unit = 'min';
|
||||
} else if (seconds < day) {
|
||||
value = Math.floor(seconds / hour);
|
||||
unit = 'hr';
|
||||
} else if (seconds < week) {
|
||||
value = Math.floor(seconds / day);
|
||||
unit = 'day';
|
||||
} else if (seconds < month) {
|
||||
value = Math.floor(seconds / week);
|
||||
unit = 'wk';
|
||||
} else if (seconds < year) {
|
||||
value = Math.floor(seconds / month);
|
||||
unit = 'mo';
|
||||
} else {
|
||||
value = Math.floor(seconds / year);
|
||||
unit = 'yr';
|
||||
}
|
||||
|
||||
return `${value} ${unit}${value === 1 ? '' : 's'}`;
|
||||
|
||||
}
|
||||
|
||||
export function toLocalISOString(date: Date): string {
|
||||
return date.getFullYear() +
|
||||
'-' + pad(date.getMonth() + 1, 2) +
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
|
||||
export interface ILink {
|
||||
readonly label: string;
|
||||
readonly href: string;
|
||||
@@ -10,7 +12,16 @@ export interface ILink {
|
||||
}
|
||||
|
||||
export type LinkedTextNode = string | ILink;
|
||||
export type LinkedText = LinkedTextNode[];
|
||||
|
||||
export class LinkedText {
|
||||
|
||||
constructor(readonly nodes: LinkedTextNode[]) { }
|
||||
|
||||
@memoize
|
||||
toString(): string {
|
||||
return this.nodes.map(node => typeof node === 'string' ? node : node.label).join('');
|
||||
}
|
||||
}
|
||||
|
||||
const LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
|
||||
|
||||
@@ -40,5 +51,5 @@ export function parseLinkedText(text: string): LinkedText {
|
||||
result.push(text.substring(index));
|
||||
}
|
||||
|
||||
return result;
|
||||
return new LinkedText(result);
|
||||
}
|
||||
+13
-13
@@ -4,50 +4,50 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { parseLinkedText } from 'vs/base/browser/linkedText';
|
||||
import { parseLinkedText } from 'vs/base/common/linkedText';
|
||||
|
||||
suite('LinkedText', () => {
|
||||
test('parses correctly', () => {
|
||||
assert.deepEqual(parseLinkedText(''), []);
|
||||
assert.deepEqual(parseLinkedText('hello'), ['hello']);
|
||||
assert.deepEqual(parseLinkedText('hello there'), ['hello there']);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).'), [
|
||||
assert.deepEqual(parseLinkedText('').nodes, []);
|
||||
assert.deepEqual(parseLinkedText('hello').nodes, ['hello']);
|
||||
assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'link text', href: 'http://link.href' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
|
||||
'Some message with [link text](random stuff).'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'https link', href: 'https://link.href' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [https link](https:).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [https link](https:).').nodes, [
|
||||
'Some message with [https link](https:).'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [
|
||||
'Some message with ',
|
||||
{ label: 'a command', href: 'command:foobar' },
|
||||
'.'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:).'), [
|
||||
assert.deepEqual(parseLinkedText('Some message with [a command](command:).').nodes, [
|
||||
'Some message with [a command](command:).'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...'), [
|
||||
assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...').nodes, [
|
||||
'link ',
|
||||
{ label: 'one', href: 'command:foo', title: 'nice' },
|
||||
' and link ',
|
||||
{ label: 'two', href: 'http://foo' },
|
||||
'...'
|
||||
]);
|
||||
assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...'), [
|
||||
assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...').nodes, [
|
||||
'link\n',
|
||||
{ label: 'one', href: 'command:foo', title: 'nice' },
|
||||
'\nand link ',
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLinkProvider, IApplicationLink } from 'vs/workbench/workbench.web.api';
|
||||
import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IApplicationLink } from 'vs/workbench/workbench.web.api';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { streamToBuffer } from 'vs/base/common/buffer';
|
||||
@@ -279,39 +279,6 @@ class WorkspaceProvider implements IWorkspaceProvider {
|
||||
}
|
||||
}
|
||||
|
||||
class ApplicationLinkProvider {
|
||||
|
||||
private links: IApplicationLink[] | undefined = undefined;
|
||||
|
||||
constructor(workspace: IWorkspace) {
|
||||
this.computeLink(workspace);
|
||||
}
|
||||
|
||||
private computeLink(workspace: IWorkspace): void {
|
||||
if (!workspace) {
|
||||
return; // not for empty workspaces
|
||||
}
|
||||
|
||||
const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined;
|
||||
if (workspaceUri) {
|
||||
this.links = [{
|
||||
uri: URI.from({
|
||||
scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders',
|
||||
authority: Schemas.vscodeRemote,
|
||||
path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path),
|
||||
query: workspaceUri.query,
|
||||
fragment: workspaceUri.fragment,
|
||||
}),
|
||||
label: localize('openInDesktop', "Open in Desktop")
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
get provider(): IApplicationLinkProvider {
|
||||
return () => this.links;
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
|
||||
// Find config by checking for DOM
|
||||
@@ -375,12 +342,30 @@ class ApplicationLinkProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// Application links ("Open in Desktop")
|
||||
let applicationLinks: IApplicationLink[] | undefined = undefined;
|
||||
if (workspace) {
|
||||
const workspaceUri = isWorkspaceToOpen(workspace) ? workspace.workspaceUri : isFolderToOpen(workspace) ? workspace.folderUri : undefined;
|
||||
if (workspaceUri) {
|
||||
applicationLinks = [{
|
||||
uri: URI.from({
|
||||
scheme: product.quality === 'stable' ? 'vscode' : 'vscode-insiders',
|
||||
authority: Schemas.vscodeRemote,
|
||||
path: posix.join(posix.sep, workspaceUri.authority, workspaceUri.path),
|
||||
query: workspaceUri.query,
|
||||
fragment: workspaceUri.fragment,
|
||||
}),
|
||||
label: localize('openInDesktop', "Open in Desktop")
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Finally create workbench
|
||||
create(document.body, {
|
||||
...config,
|
||||
workspaceProvider: new WorkspaceProvider(workspace, payload),
|
||||
urlCallbackProvider: new PollingURLCallbackProvider(),
|
||||
credentialsProvider: new LocalStorageCredentialsProvider(),
|
||||
applicationLinkProvider: new ApplicationLinkProvider(workspace).provider
|
||||
applicationLinks: applicationLinks
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -149,6 +149,10 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe
|
||||
return result;
|
||||
}
|
||||
|
||||
const enum Constants {
|
||||
SPAN_MODULO_LIMIT = 16384
|
||||
}
|
||||
|
||||
function renderLine(lineContent: string, initialVisibleColumn: number, tabSize: number, width: number, sb: IStringBuilder): [number[], number[]] {
|
||||
sb.appendASCIIString('<div style="width:');
|
||||
sb.appendASCIIString(String(width));
|
||||
@@ -164,7 +168,11 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
|
||||
let visibleColumns: number[] = [];
|
||||
let nextCharCode = (0 < len ? lineContent.charCodeAt(0) : CharCode.Null);
|
||||
|
||||
sb.appendASCIIString('<span>');
|
||||
for (let charIndex = 0; charIndex < len; charIndex++) {
|
||||
if (charIndex !== 0 && charIndex % Constants.SPAN_MODULO_LIMIT === 0) {
|
||||
sb.appendASCIIString('</span><span>');
|
||||
}
|
||||
charOffsets[charIndex] = charOffset;
|
||||
visibleColumns[charIndex] = visibleColumn;
|
||||
const charCode = nextCharCode;
|
||||
@@ -227,6 +235,7 @@ function renderLine(lineContent: string, initialVisibleColumn: number, tabSize:
|
||||
charOffset += producedCharacters;
|
||||
visibleColumn += charWidth;
|
||||
}
|
||||
sb.appendASCIIString('</span>');
|
||||
|
||||
charOffsets[lineContent.length] = charOffset;
|
||||
visibleColumns[lineContent.length] = visibleColumn;
|
||||
@@ -240,10 +249,15 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
|
||||
if (lineContent.length <= 1) {
|
||||
return null;
|
||||
}
|
||||
const textContentNode = lineDomNode.firstChild!;
|
||||
const spans = <HTMLSpanElement[]>Array.prototype.slice.call(lineDomNode.children, 0);
|
||||
|
||||
const breakOffsets: number[] = [];
|
||||
discoverBreaks(range, textContentNode, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets);
|
||||
try {
|
||||
discoverBreaks(range, spans, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (breakOffsets.length === 0) {
|
||||
return null;
|
||||
@@ -255,13 +269,13 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
|
||||
|
||||
type MaybeRects = ClientRectList | DOMRectList | null;
|
||||
|
||||
function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void {
|
||||
function discoverBreaks(range: Range, spans: HTMLSpanElement[], charOffsets: number[], low: number, lowRects: MaybeRects, high: number, highRects: MaybeRects, result: number[]): void {
|
||||
if (low === high) {
|
||||
return;
|
||||
}
|
||||
|
||||
lowRects = lowRects || readClientRect(range, textContentNode, charOffsets[low], charOffsets[low + 1]);
|
||||
highRects = highRects || readClientRect(range, textContentNode, charOffsets[high], charOffsets[high + 1]);
|
||||
lowRects = lowRects || readClientRect(range, spans, charOffsets[low], charOffsets[low + 1]);
|
||||
highRects = highRects || readClientRect(range, spans, charOffsets[high], charOffsets[high + 1]);
|
||||
|
||||
if (Math.abs(lowRects[0].top - highRects[0].top) <= 0.1) {
|
||||
// same line
|
||||
@@ -276,13 +290,13 @@ function discoverBreaks(range: Range, textContentNode: Node, charOffsets: number
|
||||
}
|
||||
|
||||
const mid = low + ((high - low) / 2) | 0;
|
||||
const midRects = readClientRect(range, textContentNode, charOffsets[mid], charOffsets[mid + 1]);
|
||||
discoverBreaks(range, textContentNode, charOffsets, low, lowRects, mid, midRects, result);
|
||||
discoverBreaks(range, textContentNode, charOffsets, mid, midRects, high, highRects, result);
|
||||
const midRects = readClientRect(range, spans, charOffsets[mid], charOffsets[mid + 1]);
|
||||
discoverBreaks(range, spans, charOffsets, low, lowRects, mid, midRects, result);
|
||||
discoverBreaks(range, spans, charOffsets, mid, midRects, high, highRects, result);
|
||||
}
|
||||
|
||||
function readClientRect(range: Range, textContentNode: Node, startOffset: number, endOffset: number): ClientRectList | DOMRectList {
|
||||
range.setStart(textContentNode, startOffset);
|
||||
range.setEnd(textContentNode, endOffset);
|
||||
function readClientRect(range: Range, spans: HTMLSpanElement[], startOffset: number, endOffset: number): ClientRectList | DOMRectList {
|
||||
range.setStart(spans[(startOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, startOffset % Constants.SPAN_MODULO_LIMIT);
|
||||
range.setEnd(spans[(endOffset / Constants.SPAN_MODULO_LIMIT) | 0].firstChild!, endOffset % Constants.SPAN_MODULO_LIMIT);
|
||||
return range.getClientRects();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,11 +34,13 @@ export class MinimapCharRenderer {
|
||||
color: RGBA8,
|
||||
backgroundColor: RGBA8,
|
||||
fontScale: number,
|
||||
useLighterFont: boolean
|
||||
useLighterFont: boolean,
|
||||
force1pxHeight: boolean
|
||||
): void {
|
||||
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
|
||||
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
|
||||
if (dx + charWidth > target.width || dy + charHeight > target.height) {
|
||||
const renderHeight = (force1pxHeight ? 1 : charHeight);
|
||||
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
@@ -60,7 +62,7 @@ export class MinimapCharRenderer {
|
||||
let sourceOffset = charIndex * charWidth * charHeight;
|
||||
|
||||
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
for (let y = 0; y < charHeight; y++) {
|
||||
for (let y = 0; y < renderHeight; y++) {
|
||||
let column = row;
|
||||
for (let x = 0; x < charWidth; x++) {
|
||||
const c = charData[sourceOffset++] / 255;
|
||||
@@ -80,11 +82,13 @@ export class MinimapCharRenderer {
|
||||
dy: number,
|
||||
color: RGBA8,
|
||||
backgroundColor: RGBA8,
|
||||
useLighterFont: boolean
|
||||
useLighterFont: boolean,
|
||||
force1pxHeight: boolean
|
||||
): void {
|
||||
const charWidth = Constants.BASE_CHAR_WIDTH * this.scale;
|
||||
const charHeight = Constants.BASE_CHAR_HEIGHT * this.scale;
|
||||
if (dx + charWidth > target.width || dy + charHeight > target.height) {
|
||||
const renderHeight = (force1pxHeight ? 1 : charHeight);
|
||||
if (dx + charWidth > target.width || dy + renderHeight > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
@@ -108,7 +112,7 @@ export class MinimapCharRenderer {
|
||||
const dest = target.data;
|
||||
|
||||
let row = dy * destWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
for (let y = 0; y < charHeight; y++) {
|
||||
for (let y = 0; y < renderHeight; y++) {
|
||||
let column = row;
|
||||
for (let x = 0; x < charWidth; x++) {
|
||||
dest[column++] = colorR;
|
||||
|
||||
@@ -276,7 +276,10 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
return true;
|
||||
}
|
||||
public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
|
||||
return true;
|
||||
if (e.affectsOverviewRuler) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onFlushed(e: viewEvents.ViewFlushedEvent): boolean {
|
||||
return true;
|
||||
|
||||
@@ -287,6 +287,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
public options!: ComputedEditorOptions;
|
||||
|
||||
private _isDominatedByLongLines: boolean;
|
||||
private _maxLineNumber: number;
|
||||
private _lineNumbersDigitCount: number;
|
||||
|
||||
private _rawOptions: IEditorOptions;
|
||||
@@ -298,6 +299,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
this.isSimpleWidget = isSimpleWidget;
|
||||
|
||||
this._isDominatedByLongLines = false;
|
||||
this._maxLineNumber = 1;
|
||||
this._lineNumbersDigitCount = 1;
|
||||
|
||||
this._rawOptions = deepCloneAndMigrateOptions(_options);
|
||||
@@ -347,6 +349,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
fontInfo: this.readConfiguration(bareFontInfo),
|
||||
extraEditorClassName: partialEnv.extraEditorClassName,
|
||||
isDominatedByLongLines: this._isDominatedByLongLines,
|
||||
maxLineNumber: this._maxLineNumber,
|
||||
lineNumbersDigitCount: this._lineNumbersDigitCount,
|
||||
emptySelectionClipboard: partialEnv.emptySelectionClipboard,
|
||||
pixelRatio: partialEnv.pixelRatio,
|
||||
@@ -405,11 +408,11 @@ export abstract class CommonEditorConfiguration extends Disposable implements IC
|
||||
}
|
||||
|
||||
public setMaxLineNumber(maxLineNumber: number): void {
|
||||
let digitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
|
||||
if (this._lineNumbersDigitCount === digitCount) {
|
||||
if (this._maxLineNumber === maxLineNumber) {
|
||||
return;
|
||||
}
|
||||
this._lineNumbersDigitCount = digitCount;
|
||||
this._maxLineNumber = maxLineNumber;
|
||||
this._lineNumbersDigitCount = CommonEditorConfiguration._digitCount(maxLineNumber);
|
||||
this._recomputeOptions();
|
||||
}
|
||||
|
||||
|
||||
@@ -672,6 +672,7 @@ export interface IEnvironmentalOptions {
|
||||
readonly fontInfo: FontInfo;
|
||||
readonly extraEditorClassName: string;
|
||||
readonly isDominatedByLongLines: boolean;
|
||||
readonly maxLineNumber: number;
|
||||
readonly lineNumbersDigitCount: number;
|
||||
readonly emptySelectionClipboard: boolean;
|
||||
readonly pixelRatio: number;
|
||||
@@ -1685,6 +1686,14 @@ export interface EditorLayoutInfo {
|
||||
* The width of the minimap
|
||||
*/
|
||||
readonly minimapWidth: number;
|
||||
readonly minimapHeightIsEditorHeight: boolean;
|
||||
readonly minimapIsSampling: boolean;
|
||||
readonly minimapScale: number;
|
||||
readonly minimapLineHeight: number;
|
||||
readonly minimapCanvasInnerWidth: number;
|
||||
readonly minimapCanvasInnerHeight: number;
|
||||
readonly minimapCanvasOuterWidth: number;
|
||||
readonly minimapCanvasOuterHeight: number;
|
||||
|
||||
/**
|
||||
* Minimap render type
|
||||
@@ -1718,6 +1727,7 @@ export interface EditorLayoutInfoComputerEnv {
|
||||
outerWidth: number;
|
||||
outerHeight: number;
|
||||
lineHeight: number;
|
||||
maxLineNumber: number;
|
||||
lineNumbersDigitCount: number;
|
||||
typicalHalfwidthCharacterWidth: number;
|
||||
maxDigitWidth: number;
|
||||
@@ -1741,6 +1751,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
outerWidth: env.outerWidth,
|
||||
outerHeight: env.outerHeight,
|
||||
lineHeight: env.fontInfo.lineHeight,
|
||||
maxLineNumber: env.maxLineNumber,
|
||||
lineNumbersDigitCount: env.lineNumbersDigitCount,
|
||||
typicalHalfwidthCharacterWidth: env.fontInfo.typicalHalfwidthCharacterWidth,
|
||||
maxDigitWidth: env.fontInfo.maxDigitWidth,
|
||||
@@ -1748,6 +1759,20 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
});
|
||||
}
|
||||
|
||||
public static computeContainedMinimapLineCount(input: {
|
||||
modelLineCount: number;
|
||||
scrollBeyondLastLine: boolean;
|
||||
height: number;
|
||||
lineHeight: number;
|
||||
pixelRatio: number;
|
||||
}): { typicalViewportLineCount: number; extraLinesBeyondLastLine: number; desiredRatio: number; minimapLineCount: number; } {
|
||||
const typicalViewportLineCount = input.height / input.lineHeight;
|
||||
const extraLinesBeyondLastLine = input.scrollBeyondLastLine ? (typicalViewportLineCount - 1) : 0;
|
||||
const desiredRatio = (input.modelLineCount + extraLinesBeyondLastLine) / (input.pixelRatio * input.height);
|
||||
const minimapLineCount = Math.floor(input.modelLineCount / desiredRatio);
|
||||
return { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount };
|
||||
}
|
||||
|
||||
public static computeLayout(options: IComputedEditorOptions, env: EditorLayoutInfoComputerEnv): EditorLayoutInfo {
|
||||
const outerWidth = env.outerWidth | 0;
|
||||
const outerHeight = env.outerHeight | 0;
|
||||
@@ -1760,12 +1785,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
const showGlyphMargin = options.get(EditorOption.glyphMargin);
|
||||
const showLineNumbers = (options.get(EditorOption.lineNumbers).renderType !== RenderLineNumbersType.Off);
|
||||
const lineNumbersMinChars = options.get(EditorOption.lineNumbersMinChars) | 0;
|
||||
const scrollBeyondLastLine = options.get(EditorOption.scrollBeyondLastLine);
|
||||
const minimap = options.get(EditorOption.minimap);
|
||||
const minimapEnabled = minimap.enabled;
|
||||
const minimapSide = minimap.side;
|
||||
const minimapRenderCharacters = minimap.renderCharacters;
|
||||
const minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
|
||||
let minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
|
||||
const minimapMaxColumn = minimap.maxColumn | 0;
|
||||
const minimapMode = minimap.mode;
|
||||
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
const verticalScrollbarWidth = scrollbar.verticalScrollbarSize | 0;
|
||||
@@ -1805,19 +1832,65 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
|
||||
const remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
|
||||
|
||||
const baseCharHeight = minimapRenderCharacters ? 2 : 3;
|
||||
let renderMinimap: RenderMinimap;
|
||||
let minimapLeft: number;
|
||||
let minimapWidth: number;
|
||||
let minimapCanvasInnerWidth: number;
|
||||
let minimapCanvasInnerHeight = Math.floor(pixelRatio * outerHeight);
|
||||
let minimapCanvasOuterWidth: number;
|
||||
const minimapCanvasOuterHeight = minimapCanvasInnerHeight / pixelRatio;
|
||||
let minimapHeightIsEditorHeight = false;
|
||||
let minimapIsSampling = false;
|
||||
let minimapLineHeight = baseCharHeight * minimapScale;
|
||||
let contentWidth: number;
|
||||
if (!minimapEnabled) {
|
||||
minimapLeft = 0;
|
||||
minimapWidth = 0;
|
||||
minimapCanvasInnerWidth = 0;
|
||||
minimapCanvasOuterWidth = 0;
|
||||
minimapLineHeight = 1;
|
||||
renderMinimap = RenderMinimap.None;
|
||||
contentWidth = remainingWidth;
|
||||
} else {
|
||||
// The minimapScale is also the pixel width of each character. Adjust
|
||||
// for the pixel ratio of the screen.
|
||||
const minimapCharWidth = minimapScale / pixelRatio;
|
||||
let minimapCharWidth = minimapScale / pixelRatio;
|
||||
let minimapWidthMultiplier: number = 1;
|
||||
|
||||
if (minimapMode === 'cover' || minimapMode === 'contain') {
|
||||
const modelLineCount = env.maxLineNumber;
|
||||
const { typicalViewportLineCount, extraLinesBeyondLastLine, desiredRatio, minimapLineCount } = EditorLayoutInfoComputer.computeContainedMinimapLineCount({
|
||||
modelLineCount: modelLineCount,
|
||||
scrollBeyondLastLine: scrollBeyondLastLine,
|
||||
height: outerHeight,
|
||||
lineHeight: lineHeight,
|
||||
pixelRatio: pixelRatio
|
||||
});
|
||||
// ratio is intentionally not part of the layout to avoid the layout changing all the time
|
||||
// when doing sampling
|
||||
const ratio = modelLineCount / minimapLineCount;
|
||||
|
||||
if (ratio > 1) {
|
||||
minimapHeightIsEditorHeight = true;
|
||||
minimapIsSampling = true;
|
||||
minimapScale = 1;
|
||||
minimapLineHeight = 1;
|
||||
minimapCharWidth = minimapScale / pixelRatio;
|
||||
} else {
|
||||
const effectiveMinimapHeight = Math.ceil((modelLineCount + extraLinesBeyondLastLine) * minimapLineHeight);
|
||||
if (minimapMode === 'cover' || effectiveMinimapHeight > minimapCanvasInnerHeight) {
|
||||
minimapHeightIsEditorHeight = true;
|
||||
const configuredFontScale = minimapScale;
|
||||
minimapLineHeight = Math.min(lineHeight * pixelRatio, Math.max(1, Math.floor(1 / desiredRatio)));
|
||||
minimapScale = Math.min(configuredFontScale + 1, Math.max(1, Math.floor(minimapLineHeight / baseCharHeight)));
|
||||
if (minimapScale > configuredFontScale) {
|
||||
minimapWidthMultiplier = Math.min(2, minimapScale / configuredFontScale);
|
||||
}
|
||||
minimapCharWidth = minimapScale / pixelRatio / minimapWidthMultiplier;
|
||||
minimapCanvasInnerHeight = Math.ceil((Math.max(typicalViewportLineCount, modelLineCount + extraLinesBeyondLastLine)) * minimapLineHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks;
|
||||
|
||||
// Given:
|
||||
@@ -1849,6 +1922,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
} else {
|
||||
minimapLeft = outerWidth - minimapWidth - verticalScrollbarWidth;
|
||||
}
|
||||
|
||||
minimapCanvasInnerWidth = Math.floor(pixelRatio * minimapWidth);
|
||||
minimapCanvasOuterWidth = minimapCanvasInnerWidth / pixelRatio;
|
||||
minimapCanvasInnerWidth = Math.floor(minimapCanvasInnerWidth * minimapWidthMultiplier);
|
||||
}
|
||||
|
||||
// (leaving 2px for the cursor to have space after the last character)
|
||||
@@ -1875,6 +1952,14 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
renderMinimap: renderMinimap,
|
||||
minimapLeft: minimapLeft,
|
||||
minimapWidth: minimapWidth,
|
||||
minimapHeightIsEditorHeight: minimapHeightIsEditorHeight,
|
||||
minimapIsSampling: minimapIsSampling,
|
||||
minimapScale: minimapScale,
|
||||
minimapLineHeight: minimapLineHeight,
|
||||
minimapCanvasInnerWidth: minimapCanvasInnerWidth,
|
||||
minimapCanvasInnerHeight: minimapCanvasInnerHeight,
|
||||
minimapCanvasOuterWidth: minimapCanvasOuterWidth,
|
||||
minimapCanvasOuterHeight: minimapCanvasOuterHeight,
|
||||
|
||||
viewportColumn: viewportColumn,
|
||||
|
||||
@@ -1975,6 +2060,11 @@ export interface IEditorMinimapOptions {
|
||||
* Defaults to 'right'.
|
||||
*/
|
||||
side?: 'right' | 'left';
|
||||
/**
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
@@ -1990,7 +2080,6 @@ export interface IEditorMinimapOptions {
|
||||
* Defaults to 120.
|
||||
*/
|
||||
maxColumn?: number;
|
||||
|
||||
/**
|
||||
* Relative size of the font in the minimap. Defaults to 1.
|
||||
*/
|
||||
@@ -2004,6 +2093,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
constructor() {
|
||||
const defaults: EditorMinimapOptions = {
|
||||
enabled: true,
|
||||
mode: 'actual',
|
||||
side: 'right',
|
||||
showSlider: 'mouseover',
|
||||
renderCharacters: true,
|
||||
@@ -2018,6 +2108,17 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
default: defaults.enabled,
|
||||
description: nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
|
||||
},
|
||||
'editor.minimap.mode': {
|
||||
type: 'string',
|
||||
enum: ['actual', 'cover', 'contain'],
|
||||
enumDescriptions: [
|
||||
nls.localize('minimap.mode.actual', "The minimap will be displayed in its original size, so it might be higher than the editor."),
|
||||
nls.localize('minimap.mode.cover', "The minimap will always have the height of the editor and will stretch or shrink as necessary."),
|
||||
nls.localize('minimap.mode.contain', "The minimap will shrink as necessary to never be higher than the editor."),
|
||||
],
|
||||
default: defaults.mode,
|
||||
description: nls.localize('minimap.mode', "Controls the rendering mode of the minimap.")
|
||||
},
|
||||
'editor.minimap.side': {
|
||||
type: 'string',
|
||||
enum: ['left', 'right'],
|
||||
@@ -2046,7 +2147,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
type: 'number',
|
||||
default: defaults.maxColumn,
|
||||
description: nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.")
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2058,6 +2159,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
const input = _input as IEditorMinimapOptions;
|
||||
return {
|
||||
enabled: EditorBooleanOption.boolean(input.enabled, this.defaultValue.enabled),
|
||||
mode: EditorStringEnumOption.stringSet<'actual' | 'cover' | 'contain'>(input.mode, this.defaultValue.mode, ['actual', 'cover', 'contain']),
|
||||
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
|
||||
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
|
||||
@@ -1423,19 +1423,15 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
|
||||
let changeAccessor: model.IModelDecorationsChangeAccessor = {
|
||||
addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, [], [{ range: range, options: options }])[0];
|
||||
},
|
||||
changeDecoration: (id: string, newRange: IRange): void => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._changeDecorationImpl(id, newRange);
|
||||
},
|
||||
changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._changeDecorationOptionsImpl(id, _normalizeOptions(options));
|
||||
},
|
||||
removeDecoration: (id: string): void => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._deltaDecorationsImpl(ownerId, [id], []);
|
||||
},
|
||||
deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => {
|
||||
@@ -1443,7 +1439,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
// nothing to do
|
||||
return [];
|
||||
}
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
|
||||
}
|
||||
};
|
||||
@@ -1474,7 +1469,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
try {
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._onDidChangeDecorations.fire();
|
||||
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
|
||||
} finally {
|
||||
this._onDidChangeDecorations.endDeferredEmit();
|
||||
@@ -1622,6 +1616,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._decorationsTree.delete(node);
|
||||
node.reset(this.getVersionId(), startOffset, endOffset, range);
|
||||
this._decorationsTree.insert(node);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
|
||||
}
|
||||
|
||||
private _changeDecorationOptionsImpl(decorationId: string, options: ModelDecorationOptions): void {
|
||||
@@ -1633,6 +1628,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
|
||||
const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
|
||||
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(options);
|
||||
|
||||
if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) {
|
||||
// Delete + Insert due to an overview ruler status change
|
||||
this._decorationsTree.delete(node);
|
||||
@@ -1666,6 +1664,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
// (2) remove the node from the tree (if it exists)
|
||||
if (node) {
|
||||
this._decorationsTree.delete(node);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(node.options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1688,6 +1687,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
node.ownerId = ownerId;
|
||||
node.reset(versionId, startOffset, endOffset, range);
|
||||
node.setOptions(options);
|
||||
this._onDidChangeDecorations.checkAffectedAndFire(options);
|
||||
|
||||
this._decorationsTree.insert(node);
|
||||
|
||||
@@ -1713,7 +1713,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
throw new Error('Illegal value for lineNumber');
|
||||
}
|
||||
|
||||
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens);
|
||||
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens, false);
|
||||
}
|
||||
|
||||
public setTokens(tokens: MultilineTokens[]): void {
|
||||
@@ -1725,16 +1725,34 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
for (let i = 0, len = tokens.length; i < len; i++) {
|
||||
const element = tokens[i];
|
||||
ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 });
|
||||
let minChangedLineNumber = 0;
|
||||
let maxChangedLineNumber = 0;
|
||||
let hasChange = false;
|
||||
for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) {
|
||||
this.setLineTokens(element.startLineNumber + j, element.tokens[j]);
|
||||
const lineNumber = element.startLineNumber + j;
|
||||
if (hasChange) {
|
||||
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], false);
|
||||
maxChangedLineNumber = lineNumber;
|
||||
} else {
|
||||
const lineHasChange = this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), element.tokens[j], true);
|
||||
if (lineHasChange) {
|
||||
hasChange = true;
|
||||
minChangedLineNumber = lineNumber;
|
||||
maxChangedLineNumber = lineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasChange) {
|
||||
ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber });
|
||||
}
|
||||
}
|
||||
|
||||
this._emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: ranges
|
||||
});
|
||||
if (ranges.length > 0) {
|
||||
this._emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: ranges
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public setSemanticTokens(tokens: MultilineTokens2[] | null): void {
|
||||
@@ -3083,11 +3101,15 @@ export class DidChangeDecorationsEmitter extends Disposable {
|
||||
|
||||
private _deferredCnt: number;
|
||||
private _shouldFire: boolean;
|
||||
private _affectsMinimap: boolean;
|
||||
private _affectsOverviewRuler: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._deferredCnt = 0;
|
||||
this._shouldFire = false;
|
||||
this._affectsMinimap = false;
|
||||
this._affectsOverviewRuler = false;
|
||||
}
|
||||
|
||||
public beginDeferredEmit(): void {
|
||||
@@ -3098,13 +3120,31 @@ export class DidChangeDecorationsEmitter extends Disposable {
|
||||
this._deferredCnt--;
|
||||
if (this._deferredCnt === 0) {
|
||||
if (this._shouldFire) {
|
||||
const event: IModelDecorationsChangedEvent = {
|
||||
affectsMinimap: this._affectsMinimap,
|
||||
affectsOverviewRuler: this._affectsOverviewRuler,
|
||||
};
|
||||
this._shouldFire = false;
|
||||
this._actual.fire({});
|
||||
this._affectsMinimap = false;
|
||||
this._affectsOverviewRuler = false;
|
||||
this._actual.fire(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public checkAffectedAndFire(options: ModelDecorationOptions): void {
|
||||
if (!this._affectsMinimap) {
|
||||
this._affectsMinimap = options.minimap && options.minimap.position ? true : false;
|
||||
}
|
||||
if (!this._affectsOverviewRuler) {
|
||||
this._affectsOverviewRuler = options.overviewRuler && options.overviewRuler.color ? true : false;
|
||||
}
|
||||
this._shouldFire = true;
|
||||
}
|
||||
|
||||
public fire(): void {
|
||||
this._affectsMinimap = true;
|
||||
this._affectsOverviewRuler = true;
|
||||
this._shouldFire = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ export interface IModelContentChangedEvent {
|
||||
* An event describing that model decorations have changed.
|
||||
*/
|
||||
export interface IModelDecorationsChangedEvent {
|
||||
readonly affectsMinimap: boolean;
|
||||
readonly affectsOverviewRuler: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -964,10 +964,35 @@ export class TokensStore {
|
||||
this._len += insertCount;
|
||||
}
|
||||
|
||||
public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): void {
|
||||
public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean {
|
||||
const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens);
|
||||
this._ensureLine(lineIndex);
|
||||
const oldTokens = this._lineTokens[lineIndex];
|
||||
this._lineTokens[lineIndex] = tokens;
|
||||
|
||||
if (checkEquality) {
|
||||
return !TokensStore._equals(oldTokens, tokens);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) {
|
||||
if (!_a || !_b) {
|
||||
return !_a && !_b;
|
||||
}
|
||||
|
||||
const a = toUint32Array(_a);
|
||||
const b = toUint32Array(_b);
|
||||
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0, len = a.length; i < len; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//#region Editing
|
||||
|
||||
@@ -265,6 +265,34 @@ export interface HoverProvider {
|
||||
provideHover(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<Hover>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are
|
||||
* evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget.
|
||||
*/
|
||||
export interface EvaluatableExpression {
|
||||
/**
|
||||
* The range to which this expression applies.
|
||||
*/
|
||||
range: IRange;
|
||||
/*
|
||||
* This expression overrides the expression extracted from the range.
|
||||
*/
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hover provider interface defines the contract between extensions and
|
||||
* the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature.
|
||||
*/
|
||||
export interface EvaluatableExpressionProvider {
|
||||
/**
|
||||
* Provide a hover for the given position and document. Multiple hovers at the same
|
||||
* position will be merged by the editor. A hover can have a range which defaults
|
||||
* to the word range at the position when omitted.
|
||||
*/
|
||||
provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<EvaluatableExpression>;
|
||||
}
|
||||
|
||||
export const enum CompletionItemKind {
|
||||
Method,
|
||||
Function,
|
||||
@@ -1595,6 +1623,11 @@ export const SignatureHelpProviderRegistry = new LanguageFeatureRegistry<Signatu
|
||||
*/
|
||||
export const HoverProviderRegistry = new LanguageFeatureRegistry<HoverProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const EvaluatableExpressionProviderRegistry = new LanguageFeatureRegistry<EvaluatableExpressionProvider>();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ScrollType, IContentSizeChangedEvent } from 'vs/editor/common/editorCommon';
|
||||
import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
|
||||
export const enum ViewEventType {
|
||||
ViewConfigurationChanged = 1,
|
||||
@@ -82,8 +83,17 @@ export class ViewDecorationsChangedEvent {
|
||||
|
||||
public readonly type = ViewEventType.ViewDecorationsChanged;
|
||||
|
||||
constructor() {
|
||||
// Nothing to do
|
||||
readonly affectsMinimap: boolean;
|
||||
readonly affectsOverviewRuler: boolean;
|
||||
|
||||
constructor(source: IModelDecorationsChangedEvent | null) {
|
||||
if (source) {
|
||||
this.affectsMinimap = source.affectsMinimap;
|
||||
this.affectsOverviewRuler = source.affectsOverviewRuler;
|
||||
} else {
|
||||
this.affectsMinimap = true;
|
||||
this.affectsOverviewRuler = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (this.lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
|
||||
@@ -185,7 +185,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (e.hasChanged(EditorOption.readOnly)) {
|
||||
// Must read again all decorations due to readOnly filtering
|
||||
this.decorations.reset();
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
}
|
||||
|
||||
eventsCollector.emit(new viewEvents.ViewConfigurationChangedEvent(e));
|
||||
@@ -291,7 +291,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
}
|
||||
} finally {
|
||||
@@ -354,7 +354,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
const eventsCollector = this._beginEmit();
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
} finally {
|
||||
this._endEmit();
|
||||
}
|
||||
@@ -365,7 +365,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
this.decorations.onModelDecorationsChanged();
|
||||
try {
|
||||
const eventsCollector = this._beginEmit();
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(e));
|
||||
} finally {
|
||||
this._endEmit();
|
||||
}
|
||||
@@ -379,7 +379,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (lineMappingChanged) {
|
||||
eventsCollector.emit(new viewEvents.ViewFlushedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewLineMappingChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
|
||||
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent(null));
|
||||
this.decorations.onLineMappingChanged();
|
||||
this.viewLayout.onFlushed(this.getLineCount());
|
||||
this.viewLayout.onHeightMaybeChanged();
|
||||
|
||||
@@ -419,8 +419,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
|
||||
}
|
||||
if (currentMatch) {
|
||||
const ariaLabel = nls.localize('ariaSearchNoResultWithLineNum', "{0} found for '{1}', at {2}", label, searchString, currentMatch.startLineNumber + ':' + currentMatch.startColumn);
|
||||
const lineContent = this._codeEditor.getModel()?.getLineContent(currentMatch.startLineNumber);
|
||||
if (lineContent) {
|
||||
const model = this._codeEditor.getModel();
|
||||
if (model && (currentMatch.startLineNumber <= model.getLineCount()) && (currentMatch.startLineNumber >= 1)) {
|
||||
const lineContent = model.getLineContent(currentMatch.startLineNumber);
|
||||
return `${lineContent}, ${ariaLabel}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ suite('MinimapCharRenderer', () => {
|
||||
imageData.data[4 * i + 2] = background.b;
|
||||
imageData.data[4 * i + 3] = 255;
|
||||
}
|
||||
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false);
|
||||
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 2, false, false);
|
||||
|
||||
let actual: number[] = [];
|
||||
for (let i = 0; i < imageData.data.length; i++) {
|
||||
@@ -108,7 +108,7 @@ suite('MinimapCharRenderer', () => {
|
||||
imageData.data[4 * i + 3] = 255;
|
||||
}
|
||||
|
||||
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false);
|
||||
renderer.renderChar(imageData, 0, 0, 'd'.charCodeAt(0), color, background, 1, false, false);
|
||||
|
||||
let actual: number[] = [];
|
||||
for (let i = 0; i < imageData.data.length; i++) {
|
||||
|
||||
@@ -17,6 +17,7 @@ interface IEditorLayoutProviderOpts {
|
||||
readonly showLineNumbers: boolean;
|
||||
readonly lineNumbersMinChars: number;
|
||||
readonly lineNumbersDigitCount: number;
|
||||
maxLineNumber?: number;
|
||||
|
||||
readonly lineDecorationsWidth: number;
|
||||
|
||||
@@ -32,6 +33,7 @@ interface IEditorLayoutProviderOpts {
|
||||
readonly minimapSide: 'left' | 'right';
|
||||
readonly minimapRenderCharacters: boolean;
|
||||
readonly minimapMaxColumn: number;
|
||||
minimapMode?: 'actual' | 'cover' | 'contain';
|
||||
readonly pixelRatio: number;
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
options._write(EditorOption.folding, false);
|
||||
const minimapOptions: EditorMinimapOptions = {
|
||||
enabled: input.minimap,
|
||||
mode: input.minimapMode || 'actual',
|
||||
side: input.minimapSide,
|
||||
renderCharacters: input.minimapRenderCharacters,
|
||||
maxColumn: input.minimapMaxColumn,
|
||||
@@ -77,6 +80,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
outerWidth: input.outerWidth,
|
||||
outerHeight: input.outerHeight,
|
||||
lineHeight: input.lineHeight,
|
||||
maxLineNumber: input.maxLineNumber || Math.pow(10, input.lineNumbersDigitCount) - 1,
|
||||
lineNumbersDigitCount: input.lineNumbersDigitCount,
|
||||
typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth,
|
||||
maxDigitWidth: input.maxDigitWidth,
|
||||
@@ -125,6 +129,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 98,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -179,6 +191,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 97,
|
||||
|
||||
verticalScrollbarWidth: 11,
|
||||
@@ -233,6 +253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -287,6 +315,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -341,6 +377,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 88,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -395,6 +439,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -449,6 +501,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -503,6 +563,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 82,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -557,6 +625,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 171,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -611,6 +687,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.None,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 0,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 0,
|
||||
minimapCanvasInnerHeight: 900,
|
||||
minimapCanvasOuterWidth: 0,
|
||||
minimapCanvasOuterHeight: 900,
|
||||
viewportColumn: 169,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -665,6 +749,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 903,
|
||||
minimapWidth: 97,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 2,
|
||||
minimapCanvasInnerWidth: 97,
|
||||
minimapCanvasInnerHeight: 800,
|
||||
minimapCanvasOuterWidth: 97,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 89,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -719,6 +811,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 903,
|
||||
minimapWidth: 97,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 194,
|
||||
minimapCanvasInnerHeight: 1600,
|
||||
minimapCanvasOuterWidth: 97,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 89,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -773,6 +873,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 945,
|
||||
minimapWidth: 55,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 220,
|
||||
minimapCanvasInnerHeight: 3200,
|
||||
minimapCanvasOuterWidth: 55,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 93,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -827,6 +935,270 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 0,
|
||||
minimapWidth: 55,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 220,
|
||||
minimapCanvasInnerHeight: 3200,
|
||||
minimapCanvasOuterWidth: 55,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 93,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
|
||||
overviewRuler: {
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 800,
|
||||
right: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('EditorLayoutProvider 11 - minimap mode cover without sampling', () => {
|
||||
doTest({
|
||||
outerWidth: 1000,
|
||||
outerHeight: 800,
|
||||
showGlyphMargin: false,
|
||||
lineHeight: 16,
|
||||
showLineNumbers: false,
|
||||
lineNumbersMinChars: 0,
|
||||
lineNumbersDigitCount: 3,
|
||||
maxLineNumber: 120,
|
||||
lineDecorationsWidth: 10,
|
||||
typicalHalfwidthCharacterWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
scrollbarArrowSize: 0,
|
||||
verticalScrollbarHasArrows: false,
|
||||
minimap: true,
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'cover',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
|
||||
glyphMarginLeft: 0,
|
||||
glyphMarginWidth: 0,
|
||||
|
||||
lineNumbersLeft: 0,
|
||||
lineNumbersWidth: 0,
|
||||
|
||||
decorationsLeft: 0,
|
||||
decorationsWidth: 10,
|
||||
|
||||
contentLeft: 10,
|
||||
contentWidth: 893,
|
||||
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 903,
|
||||
minimapWidth: 97,
|
||||
minimapHeightIsEditorHeight: true,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 3,
|
||||
minimapLineHeight: 13,
|
||||
minimapCanvasInnerWidth: 291,
|
||||
minimapCanvasInnerHeight: 1560,
|
||||
minimapCanvasOuterWidth: 97,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 89,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
|
||||
overviewRuler: {
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 800,
|
||||
right: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('EditorLayoutProvider 12 - minimap mode cover with sampling', () => {
|
||||
doTest({
|
||||
outerWidth: 1000,
|
||||
outerHeight: 800,
|
||||
showGlyphMargin: false,
|
||||
lineHeight: 16,
|
||||
showLineNumbers: false,
|
||||
lineNumbersMinChars: 0,
|
||||
lineNumbersDigitCount: 4,
|
||||
maxLineNumber: 2500,
|
||||
lineDecorationsWidth: 10,
|
||||
typicalHalfwidthCharacterWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
scrollbarArrowSize: 0,
|
||||
verticalScrollbarHasArrows: false,
|
||||
minimap: true,
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'cover',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
|
||||
glyphMarginLeft: 0,
|
||||
glyphMarginWidth: 0,
|
||||
|
||||
lineNumbersLeft: 0,
|
||||
lineNumbersWidth: 0,
|
||||
|
||||
decorationsLeft: 0,
|
||||
decorationsWidth: 10,
|
||||
|
||||
contentLeft: 10,
|
||||
contentWidth: 935,
|
||||
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 945,
|
||||
minimapWidth: 55,
|
||||
minimapHeightIsEditorHeight: true,
|
||||
minimapIsSampling: true,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 110,
|
||||
minimapCanvasInnerHeight: 1600,
|
||||
minimapCanvasOuterWidth: 55,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 93,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
|
||||
overviewRuler: {
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 800,
|
||||
right: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('EditorLayoutProvider 13 - minimap mode contain without sampling', () => {
|
||||
doTest({
|
||||
outerWidth: 1000,
|
||||
outerHeight: 800,
|
||||
showGlyphMargin: false,
|
||||
lineHeight: 16,
|
||||
showLineNumbers: false,
|
||||
lineNumbersMinChars: 0,
|
||||
lineNumbersDigitCount: 3,
|
||||
maxLineNumber: 120,
|
||||
lineDecorationsWidth: 10,
|
||||
typicalHalfwidthCharacterWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
scrollbarArrowSize: 0,
|
||||
verticalScrollbarHasArrows: false,
|
||||
minimap: true,
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'contain',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
|
||||
glyphMarginLeft: 0,
|
||||
glyphMarginWidth: 0,
|
||||
|
||||
lineNumbersLeft: 0,
|
||||
lineNumbersWidth: 0,
|
||||
|
||||
decorationsLeft: 0,
|
||||
decorationsWidth: 10,
|
||||
|
||||
contentLeft: 10,
|
||||
contentWidth: 893,
|
||||
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 903,
|
||||
minimapWidth: 97,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 194,
|
||||
minimapCanvasInnerHeight: 1600,
|
||||
minimapCanvasOuterWidth: 97,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 89,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
|
||||
overviewRuler: {
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 800,
|
||||
right: 0
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('EditorLayoutProvider 14 - minimap mode contain with sampling', () => {
|
||||
doTest({
|
||||
outerWidth: 1000,
|
||||
outerHeight: 800,
|
||||
showGlyphMargin: false,
|
||||
lineHeight: 16,
|
||||
showLineNumbers: false,
|
||||
lineNumbersMinChars: 0,
|
||||
lineNumbersDigitCount: 4,
|
||||
maxLineNumber: 2500,
|
||||
lineDecorationsWidth: 10,
|
||||
typicalHalfwidthCharacterWidth: 10,
|
||||
maxDigitWidth: 10,
|
||||
verticalScrollbarWidth: 0,
|
||||
horizontalScrollbarHeight: 0,
|
||||
scrollbarArrowSize: 0,
|
||||
verticalScrollbarHasArrows: false,
|
||||
minimap: true,
|
||||
minimapSide: 'right',
|
||||
minimapRenderCharacters: true,
|
||||
minimapMaxColumn: 150,
|
||||
minimapMode: 'contain',
|
||||
pixelRatio: 2,
|
||||
}, {
|
||||
width: 1000,
|
||||
height: 800,
|
||||
|
||||
glyphMarginLeft: 0,
|
||||
glyphMarginWidth: 0,
|
||||
|
||||
lineNumbersLeft: 0,
|
||||
lineNumbersWidth: 0,
|
||||
|
||||
decorationsLeft: 0,
|
||||
decorationsWidth: 10,
|
||||
|
||||
contentLeft: 10,
|
||||
contentWidth: 935,
|
||||
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 945,
|
||||
minimapWidth: 55,
|
||||
minimapHeightIsEditorHeight: true,
|
||||
minimapIsSampling: true,
|
||||
minimapScale: 1,
|
||||
minimapLineHeight: 1,
|
||||
minimapCanvasInnerWidth: 110,
|
||||
minimapCanvasInnerHeight: 1600,
|
||||
minimapCanvasOuterWidth: 55,
|
||||
minimapCanvasOuterHeight: 800,
|
||||
viewportColumn: 93,
|
||||
|
||||
verticalScrollbarWidth: 0,
|
||||
@@ -881,6 +1253,14 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
|
||||
renderMinimap: RenderMinimap.Text,
|
||||
minimapLeft: 1096,
|
||||
minimapWidth: 91,
|
||||
minimapHeightIsEditorHeight: false,
|
||||
minimapIsSampling: false,
|
||||
minimapScale: 2,
|
||||
minimapLineHeight: 4,
|
||||
minimapCanvasInnerWidth: 182,
|
||||
minimapCanvasInnerHeight: 844,
|
||||
minimapCanvasOuterWidth: 91,
|
||||
minimapCanvasOuterHeight: 422,
|
||||
viewportColumn: 83,
|
||||
|
||||
verticalScrollbarWidth: 14,
|
||||
|
||||
Vendored
+40
@@ -2390,6 +2390,8 @@ declare namespace monaco.editor {
|
||||
* An event describing that model decorations have changed.
|
||||
*/
|
||||
export interface IModelDecorationsChangedEvent {
|
||||
readonly affectsMinimap: boolean;
|
||||
readonly affectsOverviewRuler: boolean;
|
||||
}
|
||||
|
||||
export interface IModelOptionsChangedEvent {
|
||||
@@ -3330,6 +3332,14 @@ declare namespace monaco.editor {
|
||||
* The width of the minimap
|
||||
*/
|
||||
readonly minimapWidth: number;
|
||||
readonly minimapHeightIsEditorHeight: boolean;
|
||||
readonly minimapIsSampling: boolean;
|
||||
readonly minimapScale: number;
|
||||
readonly minimapLineHeight: number;
|
||||
readonly minimapCanvasInnerWidth: number;
|
||||
readonly minimapCanvasInnerHeight: number;
|
||||
readonly minimapCanvasOuterWidth: number;
|
||||
readonly minimapCanvasOuterHeight: number;
|
||||
/**
|
||||
* Minimap render type
|
||||
*/
|
||||
@@ -3379,6 +3389,11 @@ declare namespace monaco.editor {
|
||||
* Defaults to 'right'.
|
||||
*/
|
||||
side?: 'right' | 'left';
|
||||
/**
|
||||
* Control the minimap rendering mode.
|
||||
* Defaults to 'actual'.
|
||||
*/
|
||||
mode?: 'actual' | 'cover' | 'contain';
|
||||
/**
|
||||
* Control the rendering of the minimap slider.
|
||||
* Defaults to 'mouseover'.
|
||||
@@ -5238,6 +5253,31 @@ declare namespace monaco.languages {
|
||||
provideHover(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<Hover>;
|
||||
}
|
||||
|
||||
/**
|
||||
* An evaluatable expression represents additional information for an expression in a document. Evaluatable expression are
|
||||
* evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget.
|
||||
*/
|
||||
export interface EvaluatableExpression {
|
||||
/**
|
||||
* The range to which this expression applies.
|
||||
*/
|
||||
range: IRange;
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hover provider interface defines the contract between extensions and
|
||||
* the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature.
|
||||
*/
|
||||
export interface EvaluatableExpressionProvider {
|
||||
/**
|
||||
* Provide a hover for the given position and document. Multiple hovers at the same
|
||||
* position will be merged by the editor. A hover can have a range which defaults
|
||||
* to the word range at the position when omitted.
|
||||
*/
|
||||
provideEvaluatableExpression(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult<EvaluatableExpression>;
|
||||
}
|
||||
|
||||
export enum CompletionItemKind {
|
||||
Method = 0,
|
||||
Function = 1,
|
||||
|
||||
@@ -114,7 +114,9 @@ export class MenuId {
|
||||
static readonly CommentActions = new MenuId('CommentActions');
|
||||
static readonly BulkEditTitle = new MenuId('BulkEditTitle');
|
||||
static readonly BulkEditContext = new MenuId('BulkEditContext');
|
||||
|
||||
static readonly TimelineItemContext = new MenuId('TimelineItemContext');
|
||||
static readonly TimelineTitle = new MenuId('TimelineTitle');
|
||||
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
|
||||
|
||||
readonly id: number;
|
||||
readonly _debugName: string;
|
||||
|
||||
@@ -29,7 +29,7 @@ export interface IConstructorSignature0<T> {
|
||||
}
|
||||
|
||||
export interface IConstructorSignature1<A1, T> {
|
||||
new(first: A1, ...services: BrandedService[]): T;
|
||||
new <Services extends BrandedService[]>(first: A1, ...services: Services): T;
|
||||
}
|
||||
|
||||
export interface IConstructorSignature2<A1, A2, T> {
|
||||
|
||||
@@ -135,15 +135,15 @@ export namespace IMarkerData {
|
||||
export function makeKeyOptionalMessage(markerData: IMarkerData, useMessage: boolean): string {
|
||||
let result: string[] = [emptyString];
|
||||
if (markerData.source) {
|
||||
result.push(markerData.source.replace('¦', '\¦'));
|
||||
result.push(markerData.source.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(emptyString);
|
||||
}
|
||||
if (markerData.code) {
|
||||
if (typeof markerData.code === 'string') {
|
||||
result.push(markerData.code.replace('¦', '\¦'));
|
||||
result.push(markerData.code.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(markerData.code.value.replace('¦', '\¦'));
|
||||
result.push(markerData.code.value.replace('¦', '\\¦'));
|
||||
}
|
||||
} else {
|
||||
result.push(emptyString);
|
||||
@@ -157,7 +157,7 @@ export namespace IMarkerData {
|
||||
// Modifed to not include the message as part of the marker key to work around
|
||||
// https://github.com/microsoft/vscode/issues/77475
|
||||
if (markerData.message && useMessage) {
|
||||
result.push(markerData.message.replace('¦', '\¦'));
|
||||
result.push(markerData.message.replace('¦', '\\¦'));
|
||||
} else {
|
||||
result.push(emptyString);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import BaseSeverity from 'vs/base/common/severity';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export import Severity = BaseSeverity;
|
||||
@@ -318,16 +318,14 @@ export class NoOpNotification implements INotificationHandle {
|
||||
|
||||
readonly progress = new NoOpProgress();
|
||||
|
||||
private readonly _onDidClose: Emitter<void> = new Emitter();
|
||||
readonly onDidClose: Event<void> = this._onDidClose.event;
|
||||
readonly onDidClose = Event.None;
|
||||
readonly onDidChangeVisibility = Event.None;
|
||||
|
||||
updateSeverity(severity: Severity): void { }
|
||||
updateMessage(message: NotificationMessage): void { }
|
||||
updateActions(actions?: INotificationActions): void { }
|
||||
|
||||
close(): void {
|
||||
this._onDidClose.dispose();
|
||||
}
|
||||
close(): void { }
|
||||
}
|
||||
|
||||
export class NoOpProgress implements INotificationProgress {
|
||||
|
||||
@@ -17,7 +17,11 @@ export interface IProgressService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
withProgress<R = any>(options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions, task: (progress: IProgress<IProgressStep>) => Promise<R>, onDidCancel?: (choice?: number) => void): Promise<R>;
|
||||
withProgress<R = any>(
|
||||
options: IProgressOptions | IProgressNotificationOptions | IProgressWindowOptions | IProgressCompositeOptions,
|
||||
task: (progress: IProgress<IProgressStep>) => Promise<R>,
|
||||
onDidCancel?: (choice?: number) => void
|
||||
): Promise<R>;
|
||||
}
|
||||
|
||||
export interface IProgressIndicator {
|
||||
@@ -45,19 +49,19 @@ export const enum ProgressLocation {
|
||||
}
|
||||
|
||||
export interface IProgressOptions {
|
||||
location: ProgressLocation | string;
|
||||
title?: string;
|
||||
source?: string;
|
||||
total?: number;
|
||||
cancellable?: boolean;
|
||||
buttons?: string[];
|
||||
readonly location: ProgressLocation | string;
|
||||
readonly title?: string;
|
||||
readonly source?: string;
|
||||
readonly total?: number;
|
||||
readonly cancellable?: boolean;
|
||||
readonly buttons?: string[];
|
||||
}
|
||||
|
||||
export interface IProgressNotificationOptions extends IProgressOptions {
|
||||
readonly location: ProgressLocation.Notification;
|
||||
readonly primaryActions?: ReadonlyArray<IAction>;
|
||||
readonly secondaryActions?: ReadonlyArray<IAction>;
|
||||
delay?: number;
|
||||
readonly delay?: number;
|
||||
}
|
||||
|
||||
export interface IProgressWindowOptions extends IProgressOptions {
|
||||
@@ -66,8 +70,8 @@ export interface IProgressWindowOptions extends IProgressOptions {
|
||||
}
|
||||
|
||||
export interface IProgressCompositeOptions extends IProgressOptions {
|
||||
location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string;
|
||||
delay?: number;
|
||||
readonly location: ProgressLocation.Explorer | ProgressLocation.Extensions | ProgressLocation.Scm | string;
|
||||
readonly delay?: number;
|
||||
}
|
||||
|
||||
export interface IProgressStep {
|
||||
@@ -96,20 +100,14 @@ export interface IProgress<T> {
|
||||
|
||||
export class Progress<T> implements IProgress<T> {
|
||||
|
||||
private _callback: (data: T) => void;
|
||||
private _value?: T;
|
||||
get value(): T | undefined { return this._value; }
|
||||
|
||||
constructor(callback: (data: T) => void) {
|
||||
this._callback = callback;
|
||||
}
|
||||
|
||||
get value(): T | undefined {
|
||||
return this._value;
|
||||
}
|
||||
constructor(private callback: (data: T) => void) { }
|
||||
|
||||
report(item: T) {
|
||||
this._value = item;
|
||||
this._callback(this._value);
|
||||
this.callback(this._value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
async triggerAutoSync(): Promise<void> {
|
||||
if (this.enabled) {
|
||||
return this.syncDelayer.trigger(() => {
|
||||
this.logService.info('Auto Sync: Triggerred.');
|
||||
this.logService.info('Auto Sync: Triggered.');
|
||||
return this.sync(false, true);
|
||||
}, this.successiveFailures
|
||||
? 1000 * 1 * Math.min(this.successiveFailures, 60) /* Delay by number of seconds as number of failures up to 1 minute */
|
||||
|
||||
Vendored
+96
-3
@@ -876,7 +876,65 @@ declare module 'vscode' {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Debug:
|
||||
//#region locate evaluatable expressions for debug hover: https://github.com/microsoft/vscode/issues/89084
|
||||
|
||||
/**
|
||||
* An EvaluatableExpression represents an expression in a document that can be evaluated by an active debugger or runtime.
|
||||
* The result of this evaluation is shown in a tooltip-like widget.
|
||||
* If only a range is specified, the expression will be extracted from the underlying document.
|
||||
* An optional expression can be used to override the extracted expression.
|
||||
* In this case the range is still used to highlight the range in the document.
|
||||
*/
|
||||
export class EvaluatableExpression {
|
||||
/*
|
||||
* The range is used to extract the evaluatable expression from the underlying document and to highlight it.
|
||||
*/
|
||||
readonly range: Range;
|
||||
/*
|
||||
* If specified the expression overrides the extracted expression.
|
||||
*/
|
||||
readonly expression?: string;
|
||||
|
||||
/**
|
||||
* Creates a new evaluatable expression object.
|
||||
*
|
||||
* @param range The range in the underlying document from which the evaluatable expression is extracted.
|
||||
* @param expression If specified overrides the extracted expression.
|
||||
*/
|
||||
constructor(range: Range, expression?: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* The evaluatable expression provider interface defines the contract between extensions and
|
||||
* the debug hover.
|
||||
*/
|
||||
export interface EvaluatableExpressionProvider {
|
||||
|
||||
/**
|
||||
* Provide an evaluatable expression for the given document and position.
|
||||
* The expression can be implicitly specified by the range in the underlying document or by explicitly returning an expression.
|
||||
*
|
||||
* @param document The document in which the command was invoked.
|
||||
* @param position The position where the command was invoked.
|
||||
* @param token A cancellation token.
|
||||
* @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be
|
||||
* signaled by returning `undefined` or `null`.
|
||||
*/
|
||||
provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult<EvaluatableExpression>;
|
||||
}
|
||||
|
||||
export namespace languages {
|
||||
/**
|
||||
* Register a provider that locates evaluatable expressions in text documents.
|
||||
*
|
||||
* If multiple providers are registered for a language an arbitrary provider will be used.
|
||||
*
|
||||
* @param selector A selector that defines the documents this provider is applicable to.
|
||||
* @param provider An evaluatable expression provider.
|
||||
* @return A [disposable](#Disposable) that unregisters this provider when being disposed.
|
||||
*/
|
||||
export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable;
|
||||
}
|
||||
|
||||
// deprecated
|
||||
|
||||
@@ -1553,6 +1611,40 @@ declare module 'vscode' {
|
||||
uri?: Uri;
|
||||
}
|
||||
|
||||
export interface TimelineCursor {
|
||||
/**
|
||||
* A provider-defined cursor specifing the range of timeline items to be returned. Must be serializable.
|
||||
*/
|
||||
cursor?: any;
|
||||
|
||||
/**
|
||||
* A flag to specify whether the timeline items requested are before or after (default) the provided cursor.
|
||||
*/
|
||||
before?: boolean;
|
||||
|
||||
/**
|
||||
* The maximum number of timeline items that should be returned.
|
||||
*/
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface Timeline {
|
||||
/**
|
||||
* A provider-defined cursor specifing the range of timeline items returned. Must be serializable.
|
||||
*/
|
||||
cursor?: any;
|
||||
|
||||
/**
|
||||
* A flag which indicates whether there are any more items that weren't returned.
|
||||
*/
|
||||
more?: boolean;
|
||||
|
||||
/**
|
||||
* An array of [timeline items](#TimelineItem).
|
||||
*/
|
||||
items: TimelineItem[];
|
||||
}
|
||||
|
||||
export interface TimelineProvider {
|
||||
/**
|
||||
* An optional event to signal that the timeline for a source has changed.
|
||||
@@ -1575,10 +1667,11 @@ declare module 'vscode' {
|
||||
*
|
||||
* @param uri The [uri](#Uri) of the file to provide the timeline for.
|
||||
* @param token A cancellation token.
|
||||
* @return An array of timeline items or a thenable that resolves to such. The lack of a result
|
||||
* @param cursor TBD
|
||||
* @return The [timeline result](#TimelineResult) or a thenable that resolves to such. The lack of a result
|
||||
* can be signaled by returning `undefined`, `null`, or an empty array.
|
||||
*/
|
||||
provideTimeline(uri: Uri, token: CancellationToken): ProviderResult<TimelineItem[]>;
|
||||
provideTimeline(uri: Uri, cursor: TimelineCursor, token: CancellationToken): ProviderResult<Timeline>;
|
||||
}
|
||||
|
||||
export namespace workspace {
|
||||
|
||||
@@ -213,6 +213,16 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
|
||||
}));
|
||||
}
|
||||
|
||||
// --- debug hover
|
||||
|
||||
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void {
|
||||
this._registrations.set(handle, modes.EvaluatableExpressionProviderRegistry.register(selector, <modes.EvaluatableExpressionProvider>{
|
||||
provideEvaluatableExpression: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined> => {
|
||||
return this._proxy.$provideEvaluatableExpression(handle, model.uri, position, token);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void {
|
||||
|
||||
@@ -3,304 +3,19 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IdleValue, raceCancellation } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
|
||||
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressService, ProgressLocation, IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ISaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ITextFileSaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface ISaveParticipantParticipant {
|
||||
participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
class TrimWhitespaceParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void {
|
||||
let prevSelection: Selection[] = [];
|
||||
let cursors: Position[] = [];
|
||||
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
|
||||
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
cursors = prevSelection.map(s => s.getPosition());
|
||||
const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange();
|
||||
if (snippetsRange) {
|
||||
for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) {
|
||||
cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ops = trimTrailingWhitespace(model, cursors);
|
||||
if (!ops.length) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection);
|
||||
}
|
||||
}
|
||||
|
||||
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null {
|
||||
let candidate: IActiveCodeEditor | null = null;
|
||||
|
||||
if (model.isAttachedToEditor()) {
|
||||
for (const editor of codeEditorService.listCodeEditors()) {
|
||||
if (editor.hasModel() && editor.getModel() === model) {
|
||||
if (editor.hasTextFocus()) {
|
||||
return editor; // favour focused editor if there are multiple
|
||||
}
|
||||
|
||||
candidate = editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
export class FinalNewLineParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doInsertFinalNewLine(model.textEditorModel);
|
||||
}
|
||||
}
|
||||
|
||||
private doInsertFinalNewLine(model: ITextModel): void {
|
||||
const lineCount = model.getLineCount();
|
||||
const lastLine = model.getLineContent(lineCount);
|
||||
const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1;
|
||||
|
||||
if (!lineCount || lastLineIsEmptyOrWhitespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edits = [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
editor.executeEdits('insertFinalNewLine', edits, editor.getSelections());
|
||||
} else {
|
||||
model.pushEditOperations([], edits, () => null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TrimFinalNewLinesParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns 0 if the entire file is empty or whitespace only
|
||||
*/
|
||||
private findLastLineWithContent(model: ITextModel): number {
|
||||
for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
if (strings.lastNonWhitespaceIndex(lineContent) !== -1) {
|
||||
// this line has content
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
// no line has content
|
||||
return 0;
|
||||
}
|
||||
|
||||
private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void {
|
||||
const lineCount = model.getLineCount();
|
||||
|
||||
// Do not insert new line if file does not end with new line
|
||||
if (lineCount === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [];
|
||||
let cannotTouchLineNumber = 0;
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
for (let i = 0, len = prevSelection.length; i < len; i++) {
|
||||
const positionLineNumber = prevSelection[i].positionLineNumber;
|
||||
if (positionLineNumber > cannotTouchLineNumber) {
|
||||
cannotTouchLineNumber = positionLineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lastLineNumberWithContent = this.findLastLineWithContent(model);
|
||||
const deleteFromLineNumber = Math.max(lastLineNumberWithContent + 1, cannotTouchLineNumber + 1);
|
||||
const deletionRange = model.validateRange(new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount)));
|
||||
|
||||
if (deletionRange.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection);
|
||||
|
||||
if (editor) {
|
||||
editor.setSelections(prevSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FormatOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly _codeEditorService: ICodeEditorService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
|
||||
const model = editorModel.textEditorModel;
|
||||
const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri };
|
||||
|
||||
if (env.reason === SaveReason.AUTO || !this._configurationService.getValue('editor.formatOnSave', overrides)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
progress.report({ message: localize('formatting', "Formatting") });
|
||||
const editorOrModel = findEditor(model, this._codeEditorService) || model;
|
||||
await this._instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeActionOnSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
) { }
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
if (env.reason === SaveReason.AUTO) {
|
||||
return undefined;
|
||||
}
|
||||
const model = editorModel.textEditorModel;
|
||||
|
||||
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource };
|
||||
const setting = this._configurationService.getValue<{ [kind: string]: boolean }>('editor.codeActionsOnSave', settingsOverrides);
|
||||
if (!setting) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const codeActionsOnSave = Object.keys(setting)
|
||||
.filter(x => setting[x]).map(x => new CodeActionKind(x))
|
||||
.sort((a, b) => {
|
||||
if (CodeActionKind.SourceFixAll.contains(a)) {
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!codeActionsOnSave.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const excludedActions = Object.keys(setting)
|
||||
.filter(x => setting[x] === false)
|
||||
.map(x => new CodeActionKind(x));
|
||||
|
||||
progress.report({ message: localize('codeaction', "Quick Fixes") });
|
||||
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token);
|
||||
}
|
||||
|
||||
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise<void> {
|
||||
for (const codeActionKind of codeActionsOnSave) {
|
||||
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
|
||||
try {
|
||||
await this.applyCodeActions(actionsToRun.validActions);
|
||||
} catch {
|
||||
// Failure to apply a code action should not block other on save actions
|
||||
} finally {
|
||||
actionsToRun.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
|
||||
for (const action of actionsToRun) {
|
||||
await this._instantiationService.invokeFunction(applyCodeAction, action);
|
||||
}
|
||||
}
|
||||
|
||||
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
|
||||
return getCodeActions(model, model.getFullModelRange(), {
|
||||
type: CodeActionTriggerType.Auto,
|
||||
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
|
||||
}, token);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
class ExtHostSaveParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
private readonly _proxy: ExtHostDocumentSaveParticipantShape;
|
||||
|
||||
@@ -336,65 +51,19 @@ class ExtHostSaveParticipant implements ISaveParticipantParticipant {
|
||||
|
||||
// The save participant can change a model before its saved to support various scenarios like trimming trailing whitespace
|
||||
@extHostCustomer
|
||||
export class SaveParticipant implements ISaveParticipant {
|
||||
export class SaveParticipant {
|
||||
|
||||
private readonly _saveParticipants: IdleValue<ISaveParticipantParticipant[]>;
|
||||
private _saveParticipantDisposable: IDisposable;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IProgressService private readonly _progressService: IProgressService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@ITextFileService private readonly _textFileService: ITextFileService
|
||||
) {
|
||||
this._saveParticipants = new IdleValue(() => [
|
||||
instantiationService.createInstance(TrimWhitespaceParticipant),
|
||||
instantiationService.createInstance(CodeActionOnSaveParticipant),
|
||||
instantiationService.createInstance(FormatOnSaveParticipant),
|
||||
instantiationService.createInstance(FinalNewLineParticipant),
|
||||
instantiationService.createInstance(TrimFinalNewLinesParticipant),
|
||||
instantiationService.createInstance(ExtHostSaveParticipant, extHostContext),
|
||||
]);
|
||||
// Set as save participant for all text files
|
||||
this._textFileService.saveParticipant = this;
|
||||
this._saveParticipantDisposable = this._textFileService.files.addSaveParticipant(instantiationService.createInstance(ExtHostSaveParticipant, extHostContext));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._textFileService.saveParticipant = undefined;
|
||||
this._saveParticipants.dispose();
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, context: { reason: SaveReason; }, token: CancellationToken): Promise<void> {
|
||||
|
||||
const cts = new CancellationTokenSource(token);
|
||||
|
||||
return this._progressService.withProgress({
|
||||
title: localize('saveParticipants', "Running Save Participants for '{0}'", this._labelService.getUriLabel(model.resource, { relative: true })),
|
||||
location: ProgressLocation.Notification,
|
||||
cancellable: true,
|
||||
delay: model.isDirty() ? 3000 : 5000
|
||||
}, async progress => {
|
||||
// undoStop before participation
|
||||
model.textEditorModel.pushStackElement();
|
||||
|
||||
for (let p of this._saveParticipants.getValue()) {
|
||||
if (cts.token.isCancellationRequested) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const promise = p.participate(model, context, progress, cts.token);
|
||||
await raceCancellation(promise, cts.token);
|
||||
} catch (err) {
|
||||
this._logService.warn(err);
|
||||
}
|
||||
}
|
||||
|
||||
// undoStop after participation
|
||||
model.textEditorModel.pushStackElement();
|
||||
}, () => {
|
||||
// user cancel
|
||||
cts.dispose(true);
|
||||
});
|
||||
this._saveParticipantDisposable.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { MainContext, MainThreadTimelineShape, IExtHostContext, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITimelineService, TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor, ITimelineService } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTimeline)
|
||||
export class MainThreadTimeline implements MainThreadTimelineShape {
|
||||
@@ -24,10 +24,6 @@ export class MainThreadTimeline implements MainThreadTimelineShape {
|
||||
this._proxy = context.getProxy(ExtHostContext.ExtHostTimeline);
|
||||
}
|
||||
|
||||
$getTimeline(uri: URI, token: CancellationToken): Promise<TimelineItem[]> {
|
||||
return this._timelineService.getTimeline(uri, token);
|
||||
}
|
||||
|
||||
$registerTimelineProvider(provider: TimelineProviderDescriptor): void {
|
||||
this.logService.trace(`MainThreadTimeline#registerTimelineProvider: id=${provider.id}`);
|
||||
|
||||
@@ -43,8 +39,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape {
|
||||
this._timelineService.registerTimelineProvider({
|
||||
...provider,
|
||||
onDidChange: onDidChange.event,
|
||||
provideTimeline(uri: URI, token: CancellationToken) {
|
||||
return proxy.$getTimeline(provider.id, uri, token);
|
||||
provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) {
|
||||
return proxy.$getTimeline(provider.id, uri, cursor, token, options);
|
||||
},
|
||||
dispose() {
|
||||
emitters.delete(provider.id);
|
||||
|
||||
@@ -133,7 +133,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHostLabelService, new ExtHostLabelService(rpcProtocol));
|
||||
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
|
||||
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
|
||||
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol));
|
||||
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected: ProxyIdentifier<any>[] = values(ExtHostContext);
|
||||
@@ -345,6 +345,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
registerHoverProvider(selector: vscode.DocumentSelector, provider: vscode.HoverProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerHoverProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerEvaluatableExpressionProvider(selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
return extHostLanguageFeatures.registerDocumentHighlightProvider(extension, checkSelector(selector), provider);
|
||||
},
|
||||
@@ -926,6 +929,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
DocumentLink: extHostTypes.DocumentLink,
|
||||
DocumentSymbol: extHostTypes.DocumentSymbol,
|
||||
EndOfLine: extHostTypes.EndOfLine,
|
||||
EvaluatableExpression: extHostTypes.EvaluatableExpression,
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
CustomExecution: extHostTypes.CustomExecution,
|
||||
|
||||
@@ -49,7 +49,7 @@ import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TimelineItem, TimelineProviderDescriptor, TimelineChangeEvent, TimelineItemWithSource } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { Timeline, TimelineChangeEvent, TimelineCursor, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -353,6 +353,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable {
|
||||
$registerImplementationSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerHoverProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void;
|
||||
$registerQuickFixSupport(handle: number, selector: IDocumentFilterDto[], metadata: ICodeActionProviderMetadataDto): void;
|
||||
@@ -802,8 +803,6 @@ export interface MainThreadTimelineShape extends IDisposable {
|
||||
$registerTimelineProvider(provider: TimelineProviderDescriptor): void;
|
||||
$unregisterTimelineProvider(source: string): void;
|
||||
$emitTimelineChangeEvent(e: TimelineChangeEvent): void;
|
||||
|
||||
$getTimeline(uri: UriComponents, token: CancellationToken): Promise<TimelineItem[]>;
|
||||
}
|
||||
|
||||
// -- extension host
|
||||
@@ -1208,6 +1207,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
|
||||
$provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<IDefinitionLinkDto[]>;
|
||||
$provideHover(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.Hover | undefined>;
|
||||
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined>;
|
||||
$provideDocumentHighlights(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.DocumentHighlight[] | undefined>;
|
||||
$provideReferences(handle: number, resource: UriComponents, position: IPosition, context: modes.ReferenceContext, token: CancellationToken): Promise<ILocationDto[] | undefined>;
|
||||
$provideCodeActions(handle: number, resource: UriComponents, rangeOrSelection: IRange | ISelection, context: modes.CodeActionContext, token: CancellationToken): Promise<ICodeActionListDto | undefined>;
|
||||
@@ -1457,7 +1457,7 @@ export interface ExtHostTunnelServiceShape {
|
||||
}
|
||||
|
||||
export interface ExtHostTimelineShape {
|
||||
$getTimeline(source: string, uri: UriComponents, token: CancellationToken): Promise<TimelineItemWithSource[]>;
|
||||
$getTimeline(source: string, uri: UriComponents, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }): Promise<Timeline | undefined>;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
|
||||
@@ -276,6 +276,27 @@ class HoverAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class EvaluatableExpressionAdapter {
|
||||
|
||||
constructor(
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _provider: vscode.EvaluatableExpressionProvider,
|
||||
) { }
|
||||
|
||||
public provideEvaluatableExpression(resource: URI, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined> {
|
||||
|
||||
const doc = this._documents.getDocument(resource);
|
||||
const pos = typeConvert.Position.to(position);
|
||||
|
||||
return asPromise(() => this._provider.provideEvaluatableExpression(doc, pos, token)).then(value => {
|
||||
if (value) {
|
||||
return typeConvert.EvaluatableExpression.from(value);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class DocumentHighlightAdapter {
|
||||
|
||||
constructor(
|
||||
@@ -1329,7 +1350,7 @@ type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | Hov
|
||||
| RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter
|
||||
| SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter
|
||||
| TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter;
|
||||
| SelectionRangeAdapter | CallHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter | EvaluatableExpressionAdapter;
|
||||
|
||||
class AdapterData {
|
||||
constructor(
|
||||
@@ -1549,6 +1570,18 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
||||
return this._withAdapter(handle, HoverAdapter, adapter => adapter.provideHover(URI.revive(resource), position, token), undefined);
|
||||
}
|
||||
|
||||
// --- debug hover
|
||||
|
||||
registerEvaluatableExpressionProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.EvaluatableExpressionProvider, extensionId?: ExtensionIdentifier): vscode.Disposable {
|
||||
const handle = this._addNewAdapter(new EvaluatableExpressionAdapter(this._documents, provider), extension);
|
||||
this._proxy.$registerEvaluatableExpressionProvider(handle, this._transformDocumentSelector(selector));
|
||||
return this._createDisposable(handle);
|
||||
}
|
||||
|
||||
$provideEvaluatableExpression(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.EvaluatableExpression | undefined> {
|
||||
return this._withAdapter(handle, EvaluatableExpressionAdapter, adapter => adapter.provideEvaluatableExpression(URI.revive(resource), position, token), undefined);
|
||||
}
|
||||
|
||||
// --- occurrences
|
||||
|
||||
registerDocumentHighlightProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
|
||||
@@ -7,61 +7,91 @@ import * as vscode from 'vscode';
|
||||
import { UriComponents, URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TimelineItemWithSource, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { Timeline, TimelineCursor, TimelineItem, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ThemeIcon } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export interface IExtHostTimeline extends ExtHostTimelineShape {
|
||||
readonly _serviceBrand: undefined;
|
||||
$getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItemWithSource[]>;
|
||||
$getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise<Timeline | undefined>;
|
||||
}
|
||||
|
||||
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
|
||||
|
||||
export class ExtHostTimeline implements IExtHostTimeline {
|
||||
private static handlePool = 0;
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _proxy: MainThreadTimelineShape;
|
||||
|
||||
private _providers = new Map<string, TimelineProvider>();
|
||||
|
||||
private _itemsBySourceByUriMap = new Map<string | undefined, Map<string, Map<string, vscode.TimelineItem>>>();
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
commands: ExtHostCommands,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadTimeline);
|
||||
|
||||
commands.registerArgumentProcessor({
|
||||
processArgument: arg => {
|
||||
if (arg && arg.$mid === 11) {
|
||||
const uri = arg.uri === undefined ? undefined : URI.revive(arg.uri);
|
||||
return this._itemsBySourceByUriMap.get(getUriKey(uri))?.get(arg.source)?.get(arg.handle);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async $getTimeline(id: string, uri: UriComponents, token: vscode.CancellationToken): Promise<TimelineItemWithSource[]> {
|
||||
async $getTimeline(id: string, uri: UriComponents, cursor: vscode.TimelineCursor, token: vscode.CancellationToken, options?: { cacheResults?: boolean }): Promise<Timeline | undefined> {
|
||||
const provider = this._providers.get(id);
|
||||
return provider?.provideTimeline(URI.revive(uri), token) ?? [];
|
||||
return provider?.provideTimeline(URI.revive(uri), cursor, token, options);
|
||||
}
|
||||
|
||||
registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable {
|
||||
registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, _extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable {
|
||||
const timelineDisposables = new DisposableStore();
|
||||
|
||||
const convertTimelineItem = this.convertTimelineItem(provider.id, commandConverter, timelineDisposables);
|
||||
const convertTimelineItem = this.convertTimelineItem(provider.id, commandConverter, timelineDisposables).bind(this);
|
||||
|
||||
let disposable: IDisposable | undefined;
|
||||
if (provider.onDidChange) {
|
||||
disposable = provider.onDidChange(this.emitTimelineChangeEvent(provider.id), this);
|
||||
}
|
||||
|
||||
const itemsBySourceByUriMap = this._itemsBySourceByUriMap;
|
||||
return this.registerTimelineProviderCore({
|
||||
...provider,
|
||||
scheme: scheme,
|
||||
onDidChange: undefined,
|
||||
async provideTimeline(uri: URI, token: CancellationToken) {
|
||||
async provideTimeline(uri: URI, cursor: TimelineCursor, token: CancellationToken, options?: { cacheResults?: boolean }) {
|
||||
timelineDisposables.clear();
|
||||
|
||||
const results = await provider.provideTimeline(uri, token);
|
||||
// For now, only allow the caching of a single Uri
|
||||
if (options?.cacheResults && !itemsBySourceByUriMap.has(getUriKey(uri))) {
|
||||
itemsBySourceByUriMap.clear();
|
||||
}
|
||||
|
||||
const result = await provider.provideTimeline(uri, cursor, token);
|
||||
// Intentional == we don't know how a provider will respond
|
||||
// eslint-disable-next-line eqeqeq
|
||||
return results != null
|
||||
? results.map(item => convertTimelineItem(item))
|
||||
: [];
|
||||
if (result == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO: Determine if we should cache dependent on who calls us (internal vs external)
|
||||
const convertItem = convertTimelineItem(uri, options?.cacheResults ?? false);
|
||||
return {
|
||||
...result,
|
||||
source: provider.id,
|
||||
items: result.items.map(convertItem)
|
||||
};
|
||||
},
|
||||
dispose() {
|
||||
disposable?.dispose();
|
||||
@@ -70,39 +100,72 @@ export class ExtHostTimeline implements IExtHostTimeline {
|
||||
});
|
||||
}
|
||||
|
||||
private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore): (item: vscode.TimelineItem) => TimelineItemWithSource {
|
||||
return (item: vscode.TimelineItem) => {
|
||||
const { iconPath, ...props } = item;
|
||||
private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore) {
|
||||
return (uri: URI, cacheResults: boolean) => {
|
||||
let itemsMap: Map<string, vscode.TimelineItem> | undefined;
|
||||
if (cacheResults) {
|
||||
const uriKey = getUriKey(uri);
|
||||
|
||||
let icon;
|
||||
let iconDark;
|
||||
let themeIcon;
|
||||
if (item.iconPath) {
|
||||
if (iconPath instanceof ThemeIcon) {
|
||||
themeIcon = { id: iconPath.id };
|
||||
let sourceMap = this._itemsBySourceByUriMap.get(uriKey);
|
||||
if (sourceMap === undefined) {
|
||||
sourceMap = new Map();
|
||||
this._itemsBySourceByUriMap.set(uriKey, sourceMap);
|
||||
}
|
||||
else if (URI.isUri(iconPath)) {
|
||||
icon = iconPath;
|
||||
iconDark = iconPath;
|
||||
}
|
||||
else {
|
||||
({ light: icon, dark: iconDark } = iconPath as { light: URI; dark: URI });
|
||||
|
||||
itemsMap = sourceMap.get(source);
|
||||
if (itemsMap === undefined) {
|
||||
itemsMap = new Map();
|
||||
sourceMap.set(source, itemsMap);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...props,
|
||||
source: source,
|
||||
command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined,
|
||||
icon: icon,
|
||||
iconDark: iconDark,
|
||||
themeIcon: themeIcon
|
||||
return (item: vscode.TimelineItem): TimelineItem => {
|
||||
const { iconPath, ...props } = item;
|
||||
|
||||
const handle = `${source}|${item.id ?? `${item.timestamp}-${ExtHostTimeline.handlePool++}`}`;
|
||||
itemsMap?.set(handle, item);
|
||||
|
||||
let icon;
|
||||
let iconDark;
|
||||
let themeIcon;
|
||||
if (item.iconPath) {
|
||||
if (iconPath instanceof ThemeIcon) {
|
||||
themeIcon = { id: iconPath.id };
|
||||
}
|
||||
else if (URI.isUri(iconPath)) {
|
||||
icon = iconPath;
|
||||
iconDark = iconPath;
|
||||
}
|
||||
else {
|
||||
({ light: icon, dark: iconDark } = iconPath as { light: URI; dark: URI });
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...props,
|
||||
handle: handle,
|
||||
source: source,
|
||||
command: item.command ? commandConverter.toInternal(item.command, disposables) : undefined,
|
||||
icon: icon,
|
||||
iconDark: iconDark,
|
||||
themeIcon: themeIcon
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private emitTimelineChangeEvent(id: string) {
|
||||
return (e: vscode.TimelineChangeEvent) => {
|
||||
// Clear caches
|
||||
if (e?.uri === undefined) {
|
||||
for (const sourceMap of this._itemsBySourceByUriMap.values()) {
|
||||
sourceMap.get(id)?.clear();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this._itemsBySourceByUriMap.get(getUriKey(e.uri))?.clear();
|
||||
}
|
||||
|
||||
this._proxy.$emitTimelineChangeEvent({ ...e, id: id });
|
||||
};
|
||||
}
|
||||
@@ -123,9 +186,18 @@ export class ExtHostTimeline implements IExtHostTimeline {
|
||||
this._providers.set(provider.id, provider);
|
||||
|
||||
return toDisposable(() => {
|
||||
for (const sourceMap of this._itemsBySourceByUriMap.values()) {
|
||||
sourceMap.get(provider.id)?.clear();
|
||||
}
|
||||
|
||||
this._providers.delete(provider.id);
|
||||
this._proxy.$unregisterTimelineProvider(provider.id);
|
||||
provider.dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getUriKey(uri: URI | undefined): string | undefined {
|
||||
return uri?.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ export namespace MarkdownString {
|
||||
} else if (htmlContent.isMarkdownString(markup)) {
|
||||
res = markup;
|
||||
} else if (typeof markup === 'string') {
|
||||
res = { value: <string>markup };
|
||||
res = { value: markup };
|
||||
} else {
|
||||
res = { value: '' };
|
||||
}
|
||||
@@ -737,6 +737,20 @@ export namespace Hover {
|
||||
return new types.Hover(info.contents.map(MarkdownString.to), Range.to(info.range));
|
||||
}
|
||||
}
|
||||
|
||||
export namespace EvaluatableExpression {
|
||||
export function from(expression: vscode.EvaluatableExpression): modes.EvaluatableExpression {
|
||||
return <modes.EvaluatableExpression>{
|
||||
range: Range.from(expression.range),
|
||||
expression: expression.expression
|
||||
};
|
||||
}
|
||||
|
||||
export function to(info: modes.EvaluatableExpression): types.EvaluatableExpression {
|
||||
return new types.EvaluatableExpression(Range.to(info.range), info.expression);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace DocumentHighlight {
|
||||
export function from(documentHighlight: vscode.DocumentHighlight): modes.DocumentHighlight {
|
||||
return {
|
||||
|
||||
@@ -2283,6 +2283,17 @@ export class DebugAdapterInlineImplementation implements vscode.DebugAdapterInli
|
||||
}
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class EvaluatableExpression implements vscode.EvaluatableExpression {
|
||||
readonly range: vscode.Range;
|
||||
readonly expression?: string;
|
||||
|
||||
constructor(range: vscode.Range, expression?: string) {
|
||||
this.range = range;
|
||||
this.expression = expression;
|
||||
}
|
||||
}
|
||||
|
||||
export enum LogLevel {
|
||||
Trace = 1,
|
||||
Debug = 2,
|
||||
|
||||
@@ -52,6 +52,8 @@ namespace schema {
|
||||
case 'comments/comment/title': return MenuId.CommentTitle;
|
||||
case 'comments/comment/context': return MenuId.CommentActions;
|
||||
case 'extension/context': return MenuId.ExtensionContext;
|
||||
case 'timeline/title': return MenuId.TimelineTitle;
|
||||
case 'timeline/item/context': return MenuId.TimelineItemContext;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -215,6 +217,16 @@ namespace schema {
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'timeline/title': {
|
||||
description: localize('view.timelineTitle', "The Timeline view title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'timeline/item/context': {
|
||||
description: localize('view.timelineContext', "The Timeline view item context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ enum Redraw {
|
||||
class ResourceLabelWidget extends IconLabel {
|
||||
|
||||
private _onDidRender = this._register(new Emitter<void>());
|
||||
readonly onDidRender: Event<void> = this._onDidRender.event;
|
||||
readonly onDidRender = this._onDidRender.event;
|
||||
|
||||
private readonly renderDisposables = this._register(new DisposableStore());
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { EventType, addDisposableListener, addClass, removeClass, isAncestor, getClientArea, Dimension, toggleClass, position, size } from 'vs/base/browser/dom';
|
||||
import { onDidChangeFullscreen, isFullscreen } from 'vs/base/browser/browser';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
@@ -87,26 +87,26 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
|
||||
//#region Events
|
||||
|
||||
private readonly _onZenModeChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onZenModeChange: Event<boolean> = this._onZenModeChange.event;
|
||||
private readonly _onZenModeChange = this._register(new Emitter<boolean>());
|
||||
readonly onZenModeChange = this._onZenModeChange.event;
|
||||
|
||||
private readonly _onFullscreenChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onFullscreenChange: Event<boolean> = this._onFullscreenChange.event;
|
||||
private readonly _onFullscreenChange = this._register(new Emitter<boolean>());
|
||||
readonly onFullscreenChange = this._onFullscreenChange.event;
|
||||
|
||||
private readonly _onCenteredLayoutChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onCenteredLayoutChange: Event<boolean> = this._onCenteredLayoutChange.event;
|
||||
private readonly _onCenteredLayoutChange = this._register(new Emitter<boolean>());
|
||||
readonly onCenteredLayoutChange = this._onCenteredLayoutChange.event;
|
||||
|
||||
private readonly _onMaximizeChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onMaximizeChange: Event<boolean> = this._onMaximizeChange.event;
|
||||
private readonly _onMaximizeChange = this._register(new Emitter<boolean>());
|
||||
readonly onMaximizeChange = this._onMaximizeChange.event;
|
||||
|
||||
private readonly _onPanelPositionChange: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onPanelPositionChange: Event<string> = this._onPanelPositionChange.event;
|
||||
private readonly _onPanelPositionChange = this._register(new Emitter<string>());
|
||||
readonly onPanelPositionChange = this._onPanelPositionChange.event;
|
||||
|
||||
private readonly _onPartVisibilityChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onPartVisibilityChange: Event<void> = this._onPartVisibilityChange.event;
|
||||
private readonly _onPartVisibilityChange = this._register(new Emitter<void>());
|
||||
readonly onPartVisibilityChange = this._onPartVisibilityChange.event;
|
||||
|
||||
private readonly _onLayout = this._register(new Emitter<IDimension>());
|
||||
readonly onLayout: Event<IDimension> = this._onLayout.event;
|
||||
readonly onLayout = this._onLayout.event;
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export abstract class Part extends Component implements ISerializableView {
|
||||
get dimension(): Dimension | undefined { return this._dimension; }
|
||||
|
||||
protected _onDidVisibilityChange = this._register(new Emitter<boolean>());
|
||||
readonly onDidVisibilityChange: Event<boolean> = this._onDidVisibilityChange.event;
|
||||
readonly onDidVisibilityChange = this._onDidVisibilityChange.event;
|
||||
|
||||
private parent: HTMLElement | undefined;
|
||||
private titleArea: HTMLElement | undefined;
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Widget } from 'vs/base/browser/ui/widget';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { LocalSelectionTransfer } from 'vs/workbench/browser/dnd';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
export interface ICompositeBarItem {
|
||||
id: string;
|
||||
@@ -61,7 +61,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
|
||||
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>;
|
||||
|
||||
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor(
|
||||
items: ICompositeBarItem[],
|
||||
|
||||
@@ -39,7 +39,7 @@ export abstract class BaseEditor extends Panel implements IEditor {
|
||||
readonly minimumHeight = DEFAULT_EDITOR_MIN_DIMENSIONS.height;
|
||||
readonly maximumHeight = DEFAULT_EDITOR_MAX_DIMENSIONS.height;
|
||||
|
||||
readonly onDidSizeConstraintsChange: Event<{ width: number; height: number; } | undefined> = Event.None;
|
||||
readonly onDidSizeConstraintsChange = Event.None;
|
||||
|
||||
protected _input: EditorInput | undefined;
|
||||
protected _options: EditorOptions | undefined;
|
||||
|
||||
@@ -1580,7 +1580,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
get maximumHeight(): number { return this.editorControl.maximumHeight; }
|
||||
|
||||
private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
|
||||
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
layout(width: number, height: number): void {
|
||||
this.dimension = new Dimension(width, height);
|
||||
|
||||
@@ -23,10 +23,10 @@ export class NotificationsAlerts extends Disposable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
private onDidChangeNotification(e: INotificationChangeEvent): void {
|
||||
if (e.kind === NotificationChangeType.ADD) {
|
||||
|
||||
// ARIA alert for screen readers
|
||||
@@ -46,7 +46,7 @@ export class NotificationsAlerts extends Disposable {
|
||||
private triggerAriaAlert(notifiation: INotificationViewItem): void {
|
||||
|
||||
// Trigger the alert again whenever the label changes
|
||||
const listener = notifiation.onDidLabelChange(e => {
|
||||
const listener = notifiation.onDidChangeLabel(e => {
|
||||
if (e.kind === NotificationViewItemLabelKind.MESSAGE) {
|
||||
this.doTriggerAriaAlert(notifiation);
|
||||
}
|
||||
@@ -69,4 +69,4 @@ export class NotificationsAlerts extends Disposable {
|
||||
|
||||
alert(alertText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export class NotificationsCenter extends Themable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
|
||||
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ export class NotificationsCenter extends Themable {
|
||||
return keybinding ? keybinding.getLabel() : null;
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
private onDidChangeNotification(e: INotificationChangeEvent): void {
|
||||
if (!this._isVisible) {
|
||||
return; // only if visible
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ export class NotificationsList extends Themable {
|
||||
}
|
||||
|
||||
// Restore DOM focus if we had focus before
|
||||
if (listHasDOMFocus) {
|
||||
if (this.isVisible && listHasDOMFocus) {
|
||||
list.domFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,11 @@ export class NotificationsStatus extends Disposable {
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidStatusMessageChange(e => this.onDidStatusMessageChange(e)));
|
||||
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
|
||||
this._register(this.model.onDidChangeStatusMessage(e => this.onDidChangeStatusMessage(e)));
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
private onDidChangeNotification(e: INotificationChangeEvent): void {
|
||||
if (this.isNotificationsCenterVisible) {
|
||||
return; // no change if notification center is visible
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export class NotificationsStatus extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private onDidStatusMessageChange(e: IStatusMessageChangeEvent): void {
|
||||
private onDidChangeStatusMessage(e: IStatusMessageChangeEvent): void {
|
||||
const statusItem = e.item;
|
||||
|
||||
switch (e.kind) {
|
||||
|
||||
@@ -90,11 +90,11 @@ export class NotificationsToasts extends Themable {
|
||||
this.model.notifications.forEach(notification => this.addToast(notification));
|
||||
|
||||
// Update toasts on notification changes
|
||||
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
|
||||
this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e)));
|
||||
});
|
||||
|
||||
// Filter
|
||||
this._register(this.model.onDidFilterChange(filter => {
|
||||
this._register(this.model.onDidChangeFilter(filter => {
|
||||
if (filter === NotificationsFilter.SILENT || filter === NotificationsFilter.ERROR) {
|
||||
this.hide();
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export class NotificationsToasts extends Themable {
|
||||
]);
|
||||
}
|
||||
|
||||
private onDidNotificationChange(e: INotificationChangeEvent): void {
|
||||
private onDidChangeNotification(e: INotificationChangeEvent): void {
|
||||
switch (e.kind) {
|
||||
case NotificationChangeType.ADD:
|
||||
return this.addToast(e.item);
|
||||
@@ -194,12 +194,12 @@ export class NotificationsToasts extends Themable {
|
||||
this.layoutContainer(maxDimensions.height);
|
||||
|
||||
// Update when item height changes due to expansion
|
||||
itemDisposables.add(item.onDidExpansionChange(() => {
|
||||
itemDisposables.add(item.onDidChangeExpansion(() => {
|
||||
notificationList.updateNotificationsList(0, 1, [item]);
|
||||
}));
|
||||
|
||||
// Update when item height potentially changes due to label changes
|
||||
itemDisposables.add(item.onDidLabelChange(e => {
|
||||
itemDisposables.add(item.onDidChangeLabel(e => {
|
||||
if (!item.expanded) {
|
||||
return; // dynamic height only applies to expanded notifications
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ export class NotificationTemplateRenderer extends Disposable {
|
||||
this.renderProgress(notification);
|
||||
|
||||
// Label Change Events
|
||||
this.inputDisposables.add(notification.onDidLabelChange(event => {
|
||||
this.inputDisposables.add(notification.onDidChangeLabel(event => {
|
||||
switch (event.kind) {
|
||||
case NotificationViewItemLabelKind.SEVERITY:
|
||||
this.renderSeverity(notification);
|
||||
|
||||
@@ -56,7 +56,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
|
||||
//#region IView
|
||||
|
||||
readonly minimumWidth: number = 420;
|
||||
readonly minimumWidth: number = 300;
|
||||
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
|
||||
readonly minimumHeight: number = 77;
|
||||
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
|
||||
@@ -76,7 +76,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
|
||||
//#endregion
|
||||
|
||||
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean; }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); }
|
||||
readonly onDidPanelClose: Event<IPanel> = this.onDidCompositeClose.event;
|
||||
readonly onDidPanelClose = this.onDidCompositeClose.event;
|
||||
|
||||
private activePanelContextKey: IContextKey<string>;
|
||||
private panelFocusContextKey: IContextKey<boolean>;
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .pane > .pane-body > .empty-view {
|
||||
.monaco-workbench .pane > .pane-body > .welcome-view {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 20px 0 20px;
|
||||
@@ -79,12 +79,12 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-workbench .pane > .pane-body:not(.empty) > .empty-view,
|
||||
.monaco-workbench .pane > .pane-body.empty > :not(.empty-view) {
|
||||
.monaco-workbench .pane > .pane-body:not(.welcome) > .welcome-view,
|
||||
.monaco-workbench .pane > .pane-body.welcome > :not(.welcome-view) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.monaco-workbench .pane > .pane-body > .empty-view .monaco-button {
|
||||
.monaco-workbench .pane > .pane-body > .welcome-view .monaco-button {
|
||||
max-width: 260px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
@@ -25,7 +25,7 @@ import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndControlle
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry } from 'vs/workbench/common/views';
|
||||
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor } from 'vs/workbench/common/views';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
@@ -38,7 +38,7 @@ import { Component } from 'vs/workbench/common/component';
|
||||
import { MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { ViewMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
|
||||
import { parseLinkedText } from 'vs/base/browser/linkedText';
|
||||
import { parseLinkedText } from 'vs/base/common/linkedText';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { Link } from 'vs/platform/opener/browser/link';
|
||||
@@ -60,6 +60,88 @@ export interface IViewPaneOptions extends IPaneOptions {
|
||||
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
|
||||
|
||||
interface IItem {
|
||||
readonly descriptor: IViewContentDescriptor;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
class ViewWelcomeController {
|
||||
|
||||
private _onDidChange = new Emitter<void>();
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
private defaultItem: IItem | undefined;
|
||||
private items: IItem[] = [];
|
||||
get contents(): IViewContentDescriptor[] {
|
||||
const visibleItems = this.items.filter(v => v.visible);
|
||||
|
||||
if (visibleItems.length === 0 && this.defaultItem) {
|
||||
return [this.defaultItem.descriptor];
|
||||
}
|
||||
|
||||
return visibleItems.map(v => v.descriptor);
|
||||
}
|
||||
|
||||
private contextKeyService: IContextKeyService;
|
||||
private disposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
private id: string,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
) {
|
||||
this.contextKeyService = contextKeyService.createScoped();
|
||||
this.disposables.add(this.contextKeyService);
|
||||
|
||||
contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables);
|
||||
Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables);
|
||||
this.onDidChangeViewWelcomeContent();
|
||||
}
|
||||
|
||||
private onDidChangeViewWelcomeContent(): void {
|
||||
const descriptors = viewsRegistry.getViewWelcomeContent(this.id);
|
||||
|
||||
this.items = [];
|
||||
|
||||
for (const descriptor of descriptors) {
|
||||
if (descriptor.when === 'default') {
|
||||
this.defaultItem = { descriptor, visible: true };
|
||||
} else {
|
||||
const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true;
|
||||
this.items.push({ descriptor, visible });
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
|
||||
private onDidChangeContext(): void {
|
||||
let didChange = false;
|
||||
|
||||
for (const item of this.items) {
|
||||
if (!item.descriptor.when || item.descriptor.when === 'default') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const visible = this.contextKeyService.contextMatchesRules(item.descriptor.when);
|
||||
|
||||
if (item.visible === visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.visible = visible;
|
||||
didChange = true;
|
||||
}
|
||||
|
||||
if (didChange) {
|
||||
this._onDidChange.fire();
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ViewPane extends Pane implements IView {
|
||||
|
||||
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
|
||||
@@ -76,8 +158,8 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
|
||||
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
|
||||
|
||||
protected _onDidChangeEmptyState = this._register(new Emitter<void>());
|
||||
readonly onDidChangeEmptyState: Event<void> = this._onDidChangeEmptyState.event;
|
||||
protected _onDidChangeViewWelcomeState = this._register(new Emitter<void>());
|
||||
readonly onDidChangeViewWelcomeState: Event<void> = this._onDidChangeViewWelcomeState.event;
|
||||
|
||||
private focusedViewContextKey: IContextKey<string>;
|
||||
|
||||
@@ -95,8 +177,9 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
protected twistiesContainer?: HTMLElement;
|
||||
|
||||
private bodyContainer!: HTMLElement;
|
||||
private emptyViewContainer!: HTMLElement;
|
||||
private emptyViewDisposable: IDisposable = Disposable.None;
|
||||
private viewWelcomeContainer!: HTMLElement;
|
||||
private viewWelcomeDisposable: IDisposable = Disposable.None;
|
||||
private viewWelcomeController: ViewWelcomeController;
|
||||
|
||||
constructor(
|
||||
options: IViewPaneOptions,
|
||||
@@ -119,6 +202,8 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
|
||||
this.menuActions = this._register(instantiationService.createInstance(ViewMenuActions, this.id, options.titleMenuId || MenuId.ViewTitle, MenuId.ViewTitleContext));
|
||||
this._register(this.menuActions.onDidChangeTitle(() => this.updateActions()));
|
||||
|
||||
this.viewWelcomeController = new ViewWelcomeController(this.id, contextKeyService);
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
@@ -206,18 +291,15 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
this.bodyContainer = container;
|
||||
this.emptyViewContainer = append(container, $('.empty-view', { tabIndex: 0 }));
|
||||
this.viewWelcomeContainer = append(container, $('.welcome-view', { tabIndex: 0 }));
|
||||
|
||||
// we should update our empty state whenever
|
||||
const onEmptyViewContentChange = Event.any(
|
||||
// the registry changes
|
||||
Event.map(Event.filter(viewsRegistry.onDidChangeEmptyViewContent, id => id === this.id), () => this.isEmpty()),
|
||||
// or the view's empty state changes
|
||||
Event.latch(Event.map(this.onDidChangeEmptyState, () => this.isEmpty()))
|
||||
);
|
||||
const onViewWelcomeChange = Event.any(this.viewWelcomeController.onDidChange, this.onDidChangeViewWelcomeState);
|
||||
this._register(onViewWelcomeChange(this.updateViewWelcome, this));
|
||||
this.updateViewWelcome();
|
||||
}
|
||||
|
||||
this._register(onEmptyViewContentChange(this.updateEmptyState, this));
|
||||
this.updateEmptyState(this.isEmpty());
|
||||
protected layoutBody(height: number, width: number): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
protected getProgressLocation(): string {
|
||||
@@ -286,26 +368,26 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
// Subclasses to implement for saving state
|
||||
}
|
||||
|
||||
private updateEmptyState(isEmpty: boolean): void {
|
||||
this.emptyViewDisposable.dispose();
|
||||
private updateViewWelcome(): void {
|
||||
this.viewWelcomeDisposable.dispose();
|
||||
|
||||
if (!isEmpty) {
|
||||
removeClass(this.bodyContainer, 'empty');
|
||||
this.emptyViewContainer.innerHTML = '';
|
||||
if (!this.shouldShowWelcome()) {
|
||||
removeClass(this.bodyContainer, 'welcome');
|
||||
this.viewWelcomeContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = viewsRegistry.getEmptyViewContent(this.id);
|
||||
const contents = this.viewWelcomeController.contents;
|
||||
|
||||
if (contents.length === 0) {
|
||||
removeClass(this.bodyContainer, 'empty');
|
||||
this.emptyViewContainer.innerHTML = '';
|
||||
removeClass(this.bodyContainer, 'welcome');
|
||||
this.viewWelcomeContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const disposables = new DisposableStore();
|
||||
addClass(this.bodyContainer, 'empty');
|
||||
this.emptyViewContainer.innerHTML = '';
|
||||
addClass(this.bodyContainer, 'welcome');
|
||||
this.viewWelcomeContainer.innerHTML = '';
|
||||
|
||||
for (const { content } of contents) {
|
||||
const lines = content.split('\n');
|
||||
@@ -317,13 +399,13 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
continue;
|
||||
}
|
||||
|
||||
const p = append(this.emptyViewContainer, $('p'));
|
||||
const p = append(this.viewWelcomeContainer, $('p'));
|
||||
const linkedText = parseLinkedText(line);
|
||||
|
||||
for (const node of linkedText) {
|
||||
for (const node of linkedText.nodes) {
|
||||
if (typeof node === 'string') {
|
||||
append(p, document.createTextNode(node));
|
||||
} else if (linkedText.length === 1) {
|
||||
} else if (linkedText.nodes.length === 1) {
|
||||
const button = new Button(p, { title: node.title });
|
||||
button.label = node.label;
|
||||
button.onDidClick(_ => this.openerService.open(node.href), null, disposables);
|
||||
@@ -339,10 +421,10 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
}
|
||||
}
|
||||
|
||||
this.emptyViewDisposable = disposables;
|
||||
this.viewWelcomeDisposable = disposables;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
shouldShowWelcome(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export abstract class Viewlet extends PaneComposite implements IViewlet {
|
||||
*/
|
||||
export class ViewletDescriptor extends CompositeDescriptor<Viewlet> {
|
||||
|
||||
public static create<Services extends BrandedService[]>(
|
||||
static create<Services extends BrandedService[]>(
|
||||
ctor: { new(...services: Services): Viewlet },
|
||||
id: string,
|
||||
name: string,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/workbench/browser/style';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Event, Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event';
|
||||
import { Emitter, setGlobalLeakWarningThreshold } from 'vs/base/common/event';
|
||||
import { addClasses, addClass, removeClasses } from 'vs/base/browser/dom';
|
||||
import { runWhenIdle } from 'vs/base/common/async';
|
||||
import { getZoomLevel, isFirefox, isSafari, isChrome } from 'vs/base/browser/browser';
|
||||
@@ -50,13 +50,13 @@ import { Extensions as PanelExtensions, PanelRegistry } from 'vs/workbench/brows
|
||||
export class Workbench extends Layout {
|
||||
|
||||
private readonly _onBeforeShutdown = this._register(new Emitter<BeforeShutdownEvent>());
|
||||
readonly onBeforeShutdown: Event<BeforeShutdownEvent> = this._onBeforeShutdown.event;
|
||||
readonly onBeforeShutdown = this._onBeforeShutdown.event;
|
||||
|
||||
private readonly _onWillShutdown = this._register(new Emitter<WillShutdownEvent>());
|
||||
readonly onWillShutdown: Event<WillShutdownEvent> = this._onWillShutdown.event;
|
||||
readonly onWillShutdown = this._onWillShutdown.event;
|
||||
|
||||
private readonly _onShutdown = this._register(new Emitter<void>());
|
||||
readonly onShutdown: Event<void> = this._onShutdown.event;
|
||||
readonly onShutdown = this._onShutdown.event;
|
||||
|
||||
constructor(
|
||||
parent: HTMLElement,
|
||||
|
||||
@@ -16,28 +16,29 @@ import { find, equals } from 'vs/base/common/arrays';
|
||||
|
||||
export interface INotificationsModel {
|
||||
|
||||
//
|
||||
// Notifications as Toasts/Center
|
||||
//
|
||||
//#region Notifications as Toasts/Center
|
||||
|
||||
readonly notifications: INotificationViewItem[];
|
||||
|
||||
readonly onDidNotificationChange: Event<INotificationChangeEvent>;
|
||||
readonly onDidFilterChange: Event<NotificationsFilter>;
|
||||
readonly onDidChangeNotification: Event<INotificationChangeEvent>;
|
||||
readonly onDidChangeFilter: Event<NotificationsFilter>;
|
||||
|
||||
addNotification(notification: INotification): INotificationHandle;
|
||||
|
||||
setFilter(filter: NotificationsFilter): void;
|
||||
|
||||
//
|
||||
// Notifications as Status
|
||||
//
|
||||
//#endregion
|
||||
|
||||
|
||||
//#region Notifications as Status
|
||||
|
||||
readonly statusMessage: IStatusMessageViewItem | undefined;
|
||||
|
||||
readonly onDidStatusMessageChange: Event<IStatusMessageChangeEvent>;
|
||||
readonly onDidChangeStatusMessage: Event<IStatusMessageChangeEvent>;
|
||||
|
||||
showStatusMessage(message: NotificationMessage, options?: IStatusMessageOptions): IDisposable;
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
export const enum NotificationChangeType {
|
||||
@@ -87,19 +88,22 @@ export interface IStatusMessageChangeEvent {
|
||||
kind: StatusMessageChangeType;
|
||||
}
|
||||
|
||||
export class NotificationHandle implements INotificationHandle {
|
||||
export class NotificationHandle extends Disposable implements INotificationHandle {
|
||||
|
||||
private readonly _onDidClose: Emitter<void> = new Emitter();
|
||||
readonly onDidClose: Event<void> = this._onDidClose.event;
|
||||
private readonly _onDidClose = this._register(new Emitter<void>());
|
||||
readonly onDidClose = this._onDidClose.event;
|
||||
|
||||
constructor(private readonly item: INotificationViewItem, private readonly onClose: (item: INotificationViewItem) => void) {
|
||||
super();
|
||||
|
||||
constructor(private readonly item: INotificationViewItem, private readonly closeItem: (item: INotificationViewItem) => void) {
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
Event.once(this.item.onDidClose)(() => {
|
||||
this._onDidClose.fire();
|
||||
this._onDidClose.dispose();
|
||||
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,8 +124,9 @@ export class NotificationHandle implements INotificationHandle {
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this.closeItem(this.item);
|
||||
this._onDidClose.dispose();
|
||||
this.onClose(this.item);
|
||||
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,14 +134,14 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
|
||||
private static readonly NO_OP_NOTIFICATION = new NoOpNotification();
|
||||
|
||||
private readonly _onDidNotificationChange = this._register(new Emitter<INotificationChangeEvent>());
|
||||
readonly onDidNotificationChange: Event<INotificationChangeEvent> = this._onDidNotificationChange.event;
|
||||
private readonly _onDidChangeNotification = this._register(new Emitter<INotificationChangeEvent>());
|
||||
readonly onDidChangeNotification = this._onDidChangeNotification.event;
|
||||
|
||||
private readonly _onDidStatusMessageChange = this._register(new Emitter<IStatusMessageChangeEvent>());
|
||||
readonly onDidStatusMessageChange: Event<IStatusMessageChangeEvent> = this._onDidStatusMessageChange.event;
|
||||
private readonly _onDidChangeStatusMessage = this._register(new Emitter<IStatusMessageChangeEvent>());
|
||||
readonly onDidChangeStatusMessage = this._onDidChangeStatusMessage.event;
|
||||
|
||||
private readonly _onDidFilterChange = this._register(new Emitter<NotificationsFilter>());
|
||||
readonly onDidFilterChange: Event<NotificationsFilter> = this._onDidFilterChange.event;
|
||||
private readonly _onDidChangeFilter = this._register(new Emitter<NotificationsFilter>());
|
||||
readonly onDidChangeFilter = this._onDidChangeFilter.event;
|
||||
|
||||
private readonly _notifications: INotificationViewItem[] = [];
|
||||
get notifications(): INotificationViewItem[] { return this._notifications; }
|
||||
@@ -149,7 +154,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
setFilter(filter: NotificationsFilter): void {
|
||||
this.filter = filter;
|
||||
|
||||
this._onDidFilterChange.fire(filter);
|
||||
this._onDidChangeFilter.fire(filter);
|
||||
}
|
||||
|
||||
addNotification(notification: INotification): INotificationHandle {
|
||||
@@ -168,13 +173,13 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
this._notifications.splice(0, 0, item);
|
||||
|
||||
// Events
|
||||
this._onDidNotificationChange.fire({ item, index: 0, kind: NotificationChangeType.ADD });
|
||||
this._onDidChangeNotification.fire({ item, index: 0, kind: NotificationChangeType.ADD });
|
||||
|
||||
// Wrap into handle
|
||||
return new NotificationHandle(item, item => this.closeItem(item));
|
||||
return new NotificationHandle(item, item => this.onClose(item));
|
||||
}
|
||||
|
||||
private closeItem(item: INotificationViewItem): void {
|
||||
private onClose(item: INotificationViewItem): void {
|
||||
const liveItem = this.findNotification(item);
|
||||
if (liveItem && liveItem !== item) {
|
||||
liveItem.close(); // item could have been replaced with another one, make sure to close the live item
|
||||
@@ -197,13 +202,13 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
const onItemChangeEvent = () => {
|
||||
const index = this._notifications.indexOf(item);
|
||||
if (index >= 0) {
|
||||
this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.CHANGE });
|
||||
this._onDidChangeNotification.fire({ item, index, kind: NotificationChangeType.CHANGE });
|
||||
}
|
||||
};
|
||||
|
||||
const itemExpansionChangeListener = item.onDidExpansionChange(() => onItemChangeEvent());
|
||||
const itemExpansionChangeListener = item.onDidChangeExpansion(() => onItemChangeEvent());
|
||||
|
||||
const itemLabelChangeListener = item.onDidLabelChange(e => {
|
||||
const itemLabelChangeListener = item.onDidChangeLabel(e => {
|
||||
// a label change in the area of actions or the message is a change that potentially has an impact
|
||||
// on the size of the notification and as such we emit a change event so that viewers can redraw
|
||||
if (e.kind === NotificationViewItemLabelKind.ACTIONS || e.kind === NotificationViewItemLabelKind.MESSAGE) {
|
||||
@@ -218,7 +223,7 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
const index = this._notifications.indexOf(item);
|
||||
if (index >= 0) {
|
||||
this._notifications.splice(index, 1);
|
||||
this._onDidNotificationChange.fire({ item, index, kind: NotificationChangeType.REMOVE });
|
||||
this._onDidChangeNotification.fire({ item, index, kind: NotificationChangeType.REMOVE });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -233,14 +238,14 @@ export class NotificationsModel extends Disposable implements INotificationsMode
|
||||
|
||||
// Remember as current status message and fire events
|
||||
this._statusMessage = item;
|
||||
this._onDidStatusMessageChange.fire({ kind: StatusMessageChangeType.ADD, item });
|
||||
this._onDidChangeStatusMessage.fire({ kind: StatusMessageChangeType.ADD, item });
|
||||
|
||||
return toDisposable(() => {
|
||||
|
||||
// Only reset status message if the item is still the one we had remembered
|
||||
if (this._statusMessage === item) {
|
||||
this._statusMessage = undefined;
|
||||
this._onDidStatusMessageChange.fire({ kind: StatusMessageChangeType.REMOVE, item });
|
||||
this._onDidChangeStatusMessage.fire({ kind: StatusMessageChangeType.REMOVE, item });
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -258,9 +263,9 @@ export interface INotificationViewItem {
|
||||
readonly expanded: boolean;
|
||||
readonly canCollapse: boolean;
|
||||
|
||||
readonly onDidExpansionChange: Event<void>;
|
||||
readonly onDidChangeExpansion: Event<void>;
|
||||
readonly onDidClose: Event<void>;
|
||||
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent>;
|
||||
readonly onDidChangeLabel: Event<INotificationViewItemLabelChangeEvent>;
|
||||
|
||||
expand(): void;
|
||||
collapse(skipEvents?: boolean): void;
|
||||
@@ -309,8 +314,8 @@ export interface INotificationViewItemProgress extends INotificationProgress {
|
||||
export class NotificationViewItemProgress extends Disposable implements INotificationViewItemProgress {
|
||||
private readonly _state: INotificationViewItemProgressState;
|
||||
|
||||
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
private readonly _onDidChange = this._register(new Emitter<void>());
|
||||
readonly onDidChange = this._onDidChange.event;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -405,14 +410,14 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
private _actions: INotificationActions | undefined;
|
||||
private _progress: NotificationViewItemProgress | undefined;
|
||||
|
||||
private readonly _onDidExpansionChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidExpansionChange: Event<void> = this._onDidExpansionChange.event;
|
||||
private readonly _onDidChangeExpansion = this._register(new Emitter<void>());
|
||||
readonly onDidChangeExpansion = this._onDidChangeExpansion.event;
|
||||
|
||||
private readonly _onDidClose: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidClose: Event<void> = this._onDidClose.event;
|
||||
private readonly _onDidClose = this._register(new Emitter<void>());
|
||||
readonly onDidClose = this._onDidClose.event;
|
||||
|
||||
private readonly _onDidLabelChange: Emitter<INotificationViewItemLabelChangeEvent> = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
|
||||
readonly onDidLabelChange: Event<INotificationViewItemLabelChangeEvent> = this._onDidLabelChange.event;
|
||||
private readonly _onDidChangeLabel = this._register(new Emitter<INotificationViewItemLabelChangeEvent>());
|
||||
readonly onDidChangeLabel = this._onDidChangeLabel.event;
|
||||
|
||||
static create(notification: INotification, filter: NotificationsFilter = NotificationsFilter.OFF): INotificationViewItem | undefined {
|
||||
if (!notification || !notification.message || isPromiseCanceledError(notification.message)) {
|
||||
@@ -561,7 +566,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
get progress(): INotificationViewItemProgress {
|
||||
if (!this._progress) {
|
||||
this._progress = this._register(new NotificationViewItemProgress());
|
||||
this._register(this._progress.onDidChange(() => this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.PROGRESS })));
|
||||
this._register(this._progress.onDidChange(() => this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.PROGRESS })));
|
||||
}
|
||||
|
||||
return this._progress;
|
||||
@@ -581,7 +586,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
|
||||
updateSeverity(severity: Severity): void {
|
||||
this._severity = severity;
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.SEVERITY });
|
||||
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.SEVERITY });
|
||||
}
|
||||
|
||||
updateMessage(input: NotificationMessage): void {
|
||||
@@ -591,13 +596,13 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
}
|
||||
|
||||
this._message = message;
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.MESSAGE });
|
||||
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.MESSAGE });
|
||||
}
|
||||
|
||||
updateActions(actions?: INotificationActions): void {
|
||||
this.setActions(actions);
|
||||
|
||||
this._onDidLabelChange.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
|
||||
this._onDidChangeLabel.fire({ kind: NotificationViewItemLabelKind.ACTIONS });
|
||||
}
|
||||
|
||||
expand(): void {
|
||||
@@ -606,7 +611,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
}
|
||||
|
||||
this._expanded = true;
|
||||
this._onDidExpansionChange.fire();
|
||||
this._onDidChangeExpansion.fire();
|
||||
}
|
||||
|
||||
collapse(skipEvents?: boolean): void {
|
||||
@@ -617,7 +622,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
this._expanded = false;
|
||||
|
||||
if (!skipEvents) {
|
||||
this._onDidExpansionChange.fire();
|
||||
this._onDidChangeExpansion.fire();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,8 +661,8 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
|
||||
export class ChoiceAction extends Action {
|
||||
|
||||
private readonly _onDidRun = new Emitter<void>();
|
||||
readonly onDidRun: Event<void> = this._onDidRun.event;
|
||||
private readonly _onDidRun = this._register(new Emitter<void>());
|
||||
readonly onDidRun = this._onDidRun.event;
|
||||
|
||||
private readonly _keepOpen: boolean;
|
||||
|
||||
@@ -679,12 +684,6 @@ export class ChoiceAction extends Action {
|
||||
get keepOpen(): boolean {
|
||||
return this._keepOpen;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this._onDidRun.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class StatusMessageViewItem {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { basename, extname, relativePath } from 'vs/base/common/resources';
|
||||
import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
@@ -106,8 +106,8 @@ export class ResourceGlobMatcher extends Disposable {
|
||||
|
||||
private static readonly NO_ROOT: string | null = null;
|
||||
|
||||
private readonly _onExpressionChange: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onExpressionChange: Event<void> = this._onExpressionChange.event;
|
||||
private readonly _onExpressionChange = this._register(new Emitter<void>());
|
||||
readonly onExpressionChange = this._onExpressionChange.event;
|
||||
|
||||
private readonly mapRootToParsedExpression: Map<string | null, ParsedExpression> = new Map<string, ParsedExpression>();
|
||||
private readonly mapRootToExpressionConfig: Map<string | null, IExpression> = new Map<string, IExpression>();
|
||||
|
||||
@@ -57,12 +57,12 @@ export interface IViewContainerDescriptor {
|
||||
|
||||
export interface IViewContainersRegistry {
|
||||
/**
|
||||
* An event that is triggerred when a view container is registered.
|
||||
* An event that is triggered when a view container is registered.
|
||||
*/
|
||||
readonly onDidRegister: Event<{ viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation }>;
|
||||
|
||||
/**
|
||||
* An event that is triggerred when a view container is deregistered.
|
||||
* An event that is triggered when a view container is deregistered.
|
||||
*/
|
||||
readonly onDidDeregister: Event<{ viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation }>;
|
||||
|
||||
@@ -213,6 +213,7 @@ export interface IViewDescriptorCollection extends IDisposable {
|
||||
|
||||
export interface IViewContentDescriptor {
|
||||
readonly content: string;
|
||||
readonly when?: ContextKeyExpr | 'default';
|
||||
}
|
||||
|
||||
export interface IViewsRegistry {
|
||||
@@ -235,9 +236,13 @@ export interface IViewsRegistry {
|
||||
|
||||
getViewContainer(id: string): ViewContainer | null;
|
||||
|
||||
readonly onDidChangeEmptyViewContent: Event<string>;
|
||||
registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable;
|
||||
getEmptyViewContent(id: string): IViewContentDescriptor[];
|
||||
readonly onDidChangeViewWelcomeContent: Event<string>;
|
||||
registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable;
|
||||
getViewWelcomeContent(id: string): IViewContentDescriptor[];
|
||||
}
|
||||
|
||||
function compareViewContentDescriptors(a: IViewContentDescriptor, b: IViewContentDescriptor): number {
|
||||
return a.content < b.content ? -1 : 1;
|
||||
}
|
||||
|
||||
class ViewsRegistry extends Disposable implements IViewsRegistry {
|
||||
@@ -251,12 +256,12 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
|
||||
private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._register(new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>());
|
||||
readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event;
|
||||
|
||||
private readonly _onDidChangeEmptyViewContent: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onDidChangeEmptyViewContent: Event<string> = this._onDidChangeEmptyViewContent.event;
|
||||
private readonly _onDidChangeViewWelcomeContent: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onDidChangeViewWelcomeContent: Event<string> = this._onDidChangeViewWelcomeContent.event;
|
||||
|
||||
private _viewContainers: ViewContainer[] = [];
|
||||
private _views: Map<ViewContainer, IViewDescriptor[]> = new Map<ViewContainer, IViewDescriptor[]>();
|
||||
private _emptyViewContents = new SetMap<string, IViewContentDescriptor>();
|
||||
private _viewWelcomeContents = new SetMap<string, IViewContentDescriptor>();
|
||||
|
||||
registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void {
|
||||
this.addViews(views, viewContainer);
|
||||
@@ -306,19 +311,20 @@ class ViewsRegistry extends Disposable implements IViewsRegistry {
|
||||
return null;
|
||||
}
|
||||
|
||||
registerEmptyViewContent(id: string, viewContent: IViewContentDescriptor): IDisposable {
|
||||
this._emptyViewContents.add(id, viewContent);
|
||||
this._onDidChangeEmptyViewContent.fire(id);
|
||||
registerViewWelcomeContent(id: string, viewContent: IViewContentDescriptor): IDisposable {
|
||||
this._viewWelcomeContents.add(id, viewContent);
|
||||
this._onDidChangeViewWelcomeContent.fire(id);
|
||||
|
||||
return toDisposable(() => {
|
||||
this._emptyViewContents.delete(id, viewContent);
|
||||
this._onDidChangeEmptyViewContent.fire(id);
|
||||
this._viewWelcomeContents.delete(id, viewContent);
|
||||
this._onDidChangeViewWelcomeContent.fire(id);
|
||||
});
|
||||
}
|
||||
|
||||
getEmptyViewContent(id: string): IViewContentDescriptor[] {
|
||||
getViewWelcomeContent(id: string): IViewContentDescriptor[] {
|
||||
const result: IViewContentDescriptor[] = [];
|
||||
this._emptyViewContents.forEach(id, descriptor => result.push(descriptor));
|
||||
result.sort(compareViewContentDescriptors);
|
||||
this._viewWelcomeContents.forEach(id, descriptor => result.push(descriptor));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./bulkEdit';
|
||||
import { WorkbenchAsyncDataTree, TreeResourceNavigator, IOpenEvent } from 'vs/platform/list/browser/listService';
|
||||
import { WorkspaceEdit } from 'vs/editor/common/modes';
|
||||
import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree';
|
||||
import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, BulkEditAriaProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter } from 'vs/workbench/contrib/bulkEdit/browser/bulkEditTree';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
@@ -135,6 +135,7 @@ export class BulkEditPane extends ViewPane {
|
||||
expandOnlyOnTwistieClick: true,
|
||||
multipleSelectionSupport: false,
|
||||
keyboardNavigationLabelProvider: new BulkEditNaviLabelProvider(),
|
||||
sorter: new BulkEditSorter()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -99,6 +99,15 @@ export class BulkFileOperation {
|
||||
this.newUri = edit.newUri;
|
||||
}
|
||||
}
|
||||
|
||||
needsConfirmation(): boolean {
|
||||
for (let [, edit] of this.originalEdits) {
|
||||
if (!this.parent.checked.isChecked(edit)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class BulkCategory {
|
||||
@@ -230,7 +239,7 @@ export class BulkFileOperations {
|
||||
}
|
||||
|
||||
operationByResource.forEach(value => this.fileOperations.push(value));
|
||||
operationByCategory.forEach(value => value.metadata.needsConfirmation ? this.categories.unshift(value) : this.categories.push(value));
|
||||
operationByCategory.forEach(value => this.categories.push(value));
|
||||
|
||||
// "correct" invalid parent-check child states that is
|
||||
// unchecked file edits (rename, create, delete) uncheck
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeSorter } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels';
|
||||
@@ -24,6 +24,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { WorkspaceFileEdit } from 'vs/editor/common/modes';
|
||||
import { compare } from 'vs/base/common/strings';
|
||||
|
||||
// --- VIEW MODEL
|
||||
|
||||
@@ -248,6 +249,39 @@ export class BulkEditDataSource implements IAsyncDataSource<BulkFileOperations,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class BulkEditSorter implements ITreeSorter<BulkEditElement> {
|
||||
|
||||
compare(a: BulkEditElement, b: BulkEditElement): number {
|
||||
if (a instanceof CategoryElement && b instanceof CategoryElement) {
|
||||
//
|
||||
const aConfirm = BulkEditSorter._needsConfirmation(a.category);
|
||||
const bConfirm = BulkEditSorter._needsConfirmation(b.category);
|
||||
if (aConfirm === bConfirm) {
|
||||
return a.category.metadata.label.localeCompare(b.category.metadata.label);
|
||||
} else if (aConfirm) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (a instanceof FileElement && b instanceof FileElement) {
|
||||
return compare(a.edit.uri.toString(), b.edit.uri.toString());
|
||||
}
|
||||
|
||||
if (a instanceof TextEditElement && b instanceof TextEditElement) {
|
||||
return Range.compareRangesUsingStarts(a.edit.textEdit.edit.range, b.edit.textEdit.edit.range);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static _needsConfirmation(a: BulkCategory): boolean {
|
||||
return a.fileOperations.some(ops => ops.needsConfirmation());
|
||||
}
|
||||
}
|
||||
|
||||
// --- ACCESSI
|
||||
|
||||
export class BulkEditAccessibilityProvider implements IAccessibilityProvider<BulkEditElement> {
|
||||
|
||||
@@ -9,6 +9,7 @@ import './diffEditorHelper';
|
||||
import './inspectKeybindings';
|
||||
import './largeFileOptimizations';
|
||||
import './inspectEditorTokens/inspectEditorTokens';
|
||||
import './saveParticipants';
|
||||
import './toggleMinimap';
|
||||
import './toggleMultiCursorModifier';
|
||||
import './toggleRenderControlCharacter';
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CodeAction, CodeActionTriggerType } from 'vs/editor/common/modes';
|
||||
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
|
||||
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
|
||||
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
|
||||
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
|
||||
import { IResolvedTextFileEditorModel, ITextFileService, ITextFileSaveParticipant } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { SaveReason } from 'vs/workbench/common/editor';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IWorkbenchContribution, Extensions as WorkbenchContributionsExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
|
||||
class TrimWhitespaceParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimTrailingWhitespace', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimTrailingWhitespace(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
private doTrimTrailingWhitespace(model: ITextModel, isAutoSaved: boolean): void {
|
||||
let prevSelection: Selection[] = [];
|
||||
let cursors: Position[] = [];
|
||||
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
|
||||
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
cursors = prevSelection.map(s => s.getPosition());
|
||||
const snippetsRange = SnippetController2.get(editor).getSessionEnclosingRange();
|
||||
if (snippetsRange) {
|
||||
for (let lineNumber = snippetsRange.startLineNumber; lineNumber <= snippetsRange.endLineNumber; lineNumber++) {
|
||||
cursors.push(new Position(lineNumber, model.getLineMaxColumn(lineNumber)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ops = trimTrailingWhitespace(model, cursors);
|
||||
if (!ops.length) {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, ops, (_edits) => prevSelection);
|
||||
}
|
||||
}
|
||||
|
||||
function findEditor(model: ITextModel, codeEditorService: ICodeEditorService): IActiveCodeEditor | null {
|
||||
let candidate: IActiveCodeEditor | null = null;
|
||||
|
||||
if (model.isAttachedToEditor()) {
|
||||
for (const editor of codeEditorService.listCodeEditors()) {
|
||||
if (editor.hasModel() && editor.getModel() === model) {
|
||||
if (editor.hasTextFocus()) {
|
||||
return editor; // favour focused editor if there are multiple
|
||||
}
|
||||
|
||||
candidate = editor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidate;
|
||||
}
|
||||
|
||||
export class FinalNewLineParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, _env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.insertFinalNewline', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doInsertFinalNewLine(model.textEditorModel);
|
||||
}
|
||||
}
|
||||
|
||||
private doInsertFinalNewLine(model: ITextModel): void {
|
||||
const lineCount = model.getLineCount();
|
||||
const lastLine = model.getLineContent(lineCount);
|
||||
const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1;
|
||||
|
||||
if (!lineCount || lastLineIsEmptyOrWhitespace) {
|
||||
return;
|
||||
}
|
||||
|
||||
const edits = [EditOperation.insert(new Position(lineCount, model.getLineMaxColumn(lineCount)), model.getEOL())];
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
editor.executeEdits('insertFinalNewLine', edits, editor.getSelections());
|
||||
} else {
|
||||
model.pushEditOperations([], edits, () => null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TrimFinalNewLinesParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(model: IResolvedTextFileEditorModel, env: { reason: SaveReason; }): Promise<void> {
|
||||
if (this.configurationService.getValue('files.trimFinalNewlines', { overrideIdentifier: model.textEditorModel.getLanguageIdentifier().language, resource: model.resource })) {
|
||||
this.doTrimFinalNewLines(model.textEditorModel, env.reason === SaveReason.AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns 0 if the entire file is empty or whitespace only
|
||||
*/
|
||||
private findLastLineWithContent(model: ITextModel): number {
|
||||
for (let lineNumber = model.getLineCount(); lineNumber >= 1; lineNumber--) {
|
||||
const lineContent = model.getLineContent(lineNumber);
|
||||
if (strings.lastNonWhitespaceIndex(lineContent) !== -1) {
|
||||
// this line has content
|
||||
return lineNumber;
|
||||
}
|
||||
}
|
||||
// no line has content
|
||||
return 0;
|
||||
}
|
||||
|
||||
private doTrimFinalNewLines(model: ITextModel, isAutoSaved: boolean): void {
|
||||
const lineCount = model.getLineCount();
|
||||
|
||||
// Do not insert new line if file does not end with new line
|
||||
if (lineCount === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let prevSelection: Selection[] = [];
|
||||
let cannotTouchLineNumber = 0;
|
||||
const editor = findEditor(model, this.codeEditorService);
|
||||
if (editor) {
|
||||
prevSelection = editor.getSelections();
|
||||
if (isAutoSaved) {
|
||||
for (let i = 0, len = prevSelection.length; i < len; i++) {
|
||||
const positionLineNumber = prevSelection[i].positionLineNumber;
|
||||
if (positionLineNumber > cannotTouchLineNumber) {
|
||||
cannotTouchLineNumber = positionLineNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lastLineNumberWithContent = this.findLastLineWithContent(model);
|
||||
const deleteFromLineNumber = Math.max(lastLineNumberWithContent + 1, cannotTouchLineNumber + 1);
|
||||
const deletionRange = model.validateRange(new Range(deleteFromLineNumber, 1, lineCount, model.getLineMaxColumn(lineCount)));
|
||||
|
||||
if (deletionRange.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection);
|
||||
|
||||
if (editor) {
|
||||
editor.setSelections(prevSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FormatOnSaveParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@ICodeEditorService private readonly codeEditorService: ICodeEditorService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) {
|
||||
// Nothing
|
||||
}
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
const model = editorModel.textEditorModel;
|
||||
const overrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: model.uri };
|
||||
|
||||
if (env.reason === SaveReason.AUTO || !this.configurationService.getValue('editor.formatOnSave', overrides)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
progress.report({ message: localize('formatting', "Formatting") });
|
||||
const editorOrModel = findEditor(model, this.codeEditorService) || model;
|
||||
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, token);
|
||||
}
|
||||
}
|
||||
|
||||
class CodeActionOnSaveParticipant implements ITextFileSaveParticipant {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
) { }
|
||||
|
||||
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
|
||||
if (env.reason === SaveReason.AUTO) {
|
||||
return undefined;
|
||||
}
|
||||
const model = editorModel.textEditorModel;
|
||||
|
||||
const settingsOverrides = { overrideIdentifier: model.getLanguageIdentifier().language, resource: editorModel.resource };
|
||||
const setting = this.configurationService.getValue<{ [kind: string]: boolean }>('editor.codeActionsOnSave', settingsOverrides);
|
||||
if (!setting) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const codeActionsOnSave = Object.keys(setting)
|
||||
.filter(x => setting[x]).map(x => new CodeActionKind(x))
|
||||
.sort((a, b) => {
|
||||
if (CodeActionKind.SourceFixAll.contains(a)) {
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (CodeActionKind.SourceFixAll.contains(b)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!codeActionsOnSave.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const excludedActions = Object.keys(setting)
|
||||
.filter(x => setting[x] === false)
|
||||
.map(x => new CodeActionKind(x));
|
||||
|
||||
progress.report({ message: localize('codeaction', "Quick Fixes") });
|
||||
await this.applyOnSaveActions(model, codeActionsOnSave, excludedActions, token);
|
||||
}
|
||||
|
||||
private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: readonly CodeActionKind[], excludes: readonly CodeActionKind[], token: CancellationToken): Promise<void> {
|
||||
for (const codeActionKind of codeActionsOnSave) {
|
||||
const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, token);
|
||||
try {
|
||||
await this.applyCodeActions(actionsToRun.validActions);
|
||||
} catch {
|
||||
// Failure to apply a code action should not block other on save actions
|
||||
} finally {
|
||||
actionsToRun.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async applyCodeActions(actionsToRun: readonly CodeAction[]) {
|
||||
for (const action of actionsToRun) {
|
||||
await this.instantiationService.invokeFunction(applyCodeAction, action);
|
||||
}
|
||||
}
|
||||
|
||||
private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, excludes: readonly CodeActionKind[], token: CancellationToken) {
|
||||
return getCodeActions(model, model.getFullModelRange(), {
|
||||
type: CodeActionTriggerType.Auto,
|
||||
filter: { include: codeActionKind, excludes: excludes, includeSourceActions: true },
|
||||
}, token);
|
||||
}
|
||||
}
|
||||
|
||||
export class SaveParticipantsContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService
|
||||
) {
|
||||
super();
|
||||
|
||||
this.registerSaveParticipants();
|
||||
}
|
||||
|
||||
private registerSaveParticipants(): void {
|
||||
this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(TrimWhitespaceParticipant)));
|
||||
this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(CodeActionOnSaveParticipant)));
|
||||
this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(FormatOnSaveParticipant)));
|
||||
this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(FinalNewLineParticipant)));
|
||||
this._register(this.textFileService.files.addSaveParticipant(this.instantiationService.createInstance(TrimFinalNewLinesParticipant)));
|
||||
}
|
||||
}
|
||||
|
||||
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchContributionsExtensions.Workbench);
|
||||
workbenchContributionsRegistry.registerWorkbenchContribution(SaveParticipantsContribution, LifecyclePhase.Restored);
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { FinalNewLineParticipant, TrimFinalNewLinesParticipant } from 'vs/workbench/api/browser/mainThreadSaveParticipant';
|
||||
import { FinalNewLineParticipant, TrimFinalNewLinesParticipant } from 'vs/workbench/contrib/codeEditor/browser/saveParticipants';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/electron-browser/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
@@ -40,7 +40,7 @@ CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAcces
|
||||
// #region Reopen With
|
||||
|
||||
const REOPEN_WITH_COMMAND_ID = 'reOpenWith';
|
||||
const REOPEN_WITH_TITLE = { value: nls.localize('reopenWith.title', 'Reopen With'), original: 'Reopen With' };
|
||||
const REOPEN_WITH_TITLE = { value: nls.localize('reopenWith.title', 'Reopen With...'), original: 'Reopen With' };
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: REOPEN_WITH_COMMAND_ID,
|
||||
@@ -83,6 +83,17 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
when: CONTEXT_HAS_CUSTOM_EDITORS,
|
||||
});
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: REOPEN_WITH_COMMAND_ID,
|
||||
title: REOPEN_WITH_TITLE,
|
||||
category: viewCategory,
|
||||
},
|
||||
group: '3_open',
|
||||
order: 20,
|
||||
when: CONTEXT_HAS_CUSTOM_EDITORS,
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import { FuzzyScore, createMatches } from 'vs/base/common/filters';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { ReplEvaluationResult } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
|
||||
export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
export const twistiePixels = 20;
|
||||
@@ -233,11 +232,3 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer<IExpr
|
||||
templateData.toDispose.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class BaseDebugViewPane extends ViewPane {
|
||||
|
||||
render(): void {
|
||||
super.render();
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +28,13 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { Gesture } from 'vs/base/browser/touch';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -54,7 +53,7 @@ export function getExpandedBodySize(model: IDebugModel): number {
|
||||
return Math.min(MAX_VISIBLE_BREAKPOINTS, length) * 22;
|
||||
}
|
||||
|
||||
export class BreakpointsView extends BaseDebugViewPane {
|
||||
export class BreakpointsView extends ViewPane {
|
||||
|
||||
private list!: WorkbenchList<IEnablement>;
|
||||
private needsRefresh = false;
|
||||
@@ -82,6 +81,7 @@ export class BreakpointsView extends BaseDebugViewPane {
|
||||
public renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-breakpoints');
|
||||
const delegate = new BreakpointsDelegate(this.debugService);
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
@@ -74,7 +74,7 @@ export function getContextForContributedActions(element: CallStackItem | null):
|
||||
return '';
|
||||
}
|
||||
|
||||
export class CallStackView extends BaseDebugViewPane {
|
||||
export class CallStackView extends ViewPane {
|
||||
private pauseMessage!: HTMLSpanElement;
|
||||
private pauseMessageLabel!: HTMLSpanElement;
|
||||
private onCallStackChangeScheduler: RunOnceScheduler;
|
||||
@@ -158,7 +158,7 @@ export class CallStackView extends BaseDebugViewPane {
|
||||
|
||||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-call-stack');
|
||||
const treeContainer = renderViewTree(container);
|
||||
|
||||
|
||||
@@ -136,17 +136,19 @@ function getWordToLineNumbersMap(model: ITextModel | null): Map<string, number[]
|
||||
model.forceTokenization(lineNumber);
|
||||
const lineTokens = model.getLineTokens(lineNumber);
|
||||
for (let tokenIndex = 0, tokenCount = lineTokens.getCount(); tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset);
|
||||
|
||||
// Token is a word and not a comment
|
||||
if (tokenType === StandardTokenType.Other) {
|
||||
DEFAULT_WORD_REGEXP.lastIndex = 0; // We assume tokens will usually map 1:1 to words if they match
|
||||
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const tokenStr = lineContent.substring(tokenStartOffset, tokenEndOffset);
|
||||
const wordMatch = DEFAULT_WORD_REGEXP.exec(tokenStr);
|
||||
|
||||
if (wordMatch) {
|
||||
|
||||
const word = wordMatch[0];
|
||||
if (!result.has(word)) {
|
||||
result.set(word, []);
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as dom from 'vs/base/browser/dom';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { IContentWidget, ICodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDebugService, IExpression, IExpressionContainer, IStackFrame } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -30,6 +30,8 @@ import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { VariablesRenderer } from 'vs/workbench/contrib/debug/browser/variablesView';
|
||||
import { EvaluatableExpressionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
const $ = dom.$;
|
||||
const MAX_TREE_HEIGHT = 324;
|
||||
@@ -107,6 +109,7 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
accessibilityProvider: new DebugHoverAccessibilityProvider(),
|
||||
mouseSupport: false,
|
||||
horizontalScrolling: true,
|
||||
useShadows: false,
|
||||
overrideStyles: {
|
||||
listBackground: editorHoverBackground
|
||||
}
|
||||
@@ -174,18 +177,52 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
}
|
||||
|
||||
async showAt(range: Range, focus: boolean): Promise<void> {
|
||||
const pos = range.getStartPosition();
|
||||
|
||||
const session = this.debugService.getViewModel().focusedSession;
|
||||
if (!this.editor.hasModel()) {
|
||||
|
||||
if (!session || !this.editor.hasModel()) {
|
||||
return Promise.resolve(this.hide());
|
||||
}
|
||||
|
||||
const lineContent = this.editor.getModel().getLineContent(pos.lineNumber);
|
||||
const { start, end } = getExactExpressionStartAndEnd(lineContent, range.startColumn, range.endColumn);
|
||||
// use regex to extract the sub-expression #9821
|
||||
const matchingExpression = lineContent.substring(start - 1, end);
|
||||
if (!matchingExpression || !session) {
|
||||
const model = this.editor.getModel();
|
||||
const pos = range.getStartPosition();
|
||||
|
||||
let rng: IRange | undefined = undefined;
|
||||
let matchingExpression: string | undefined;
|
||||
|
||||
if (EvaluatableExpressionProviderRegistry.has(model)) {
|
||||
const supports = EvaluatableExpressionProviderRegistry.ordered(model);
|
||||
|
||||
const promises = supports.map(support => {
|
||||
return Promise.resolve(support.provideEvaluatableExpression(model, pos, CancellationToken.None)).then(expression => {
|
||||
return expression;
|
||||
}, err => {
|
||||
//onUnexpectedExternalError(err);
|
||||
return undefined;
|
||||
});
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises).then(coalesce);
|
||||
if (results.length > 0) {
|
||||
matchingExpression = results[0].expression;
|
||||
rng = results[0].range;
|
||||
|
||||
if (!matchingExpression) {
|
||||
const lineContent = model.getLineContent(pos.lineNumber);
|
||||
matchingExpression = lineContent.substring(rng.startColumn - 1, rng.endColumn);
|
||||
}
|
||||
}
|
||||
|
||||
} else { // old one-size-fits-all strategy
|
||||
const lineContent = model.getLineContent(pos.lineNumber);
|
||||
const { start, end } = getExactExpressionStartAndEnd(lineContent, range.startColumn, range.endColumn);
|
||||
|
||||
// use regex to extract the sub-expression #9821
|
||||
matchingExpression = lineContent.substring(start - 1, end);
|
||||
rng = new Range(pos.lineNumber, start, pos.lineNumber, start + matchingExpression.length);
|
||||
}
|
||||
|
||||
if (!matchingExpression) {
|
||||
return Promise.resolve(this.hide());
|
||||
}
|
||||
|
||||
@@ -202,13 +239,15 @@ export class DebugHoverWidget implements IContentWidget {
|
||||
|
||||
if (!expression || (expression instanceof Expression && !expression.available)) {
|
||||
this.hide();
|
||||
return undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{
|
||||
range: new Range(pos.lineNumber, start, pos.lineNumber, start + matchingExpression.length),
|
||||
options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS
|
||||
}]);
|
||||
if (rng) {
|
||||
this.highlightDecorations = this.editor.deltaDecorations(this.highlightDecorations, [{
|
||||
range: rng,
|
||||
options: DebugHoverWidget._HOVER_HIGHLIGHT_DECORATION_OPTIONS
|
||||
}]);
|
||||
}
|
||||
|
||||
return this.doShow(pos, expression, focus);
|
||||
}
|
||||
|
||||
@@ -7,12 +7,12 @@ import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { normalize, isAbsolute, posix } from 'vs/base/common/path';
|
||||
import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { renderViewTree, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { renderViewTree } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -402,7 +402,7 @@ function asTreeElement(item: BaseTreeItem, viewState?: IViewState): ITreeElement
|
||||
};
|
||||
}
|
||||
|
||||
export class LoadedScriptsView extends BaseDebugViewPane {
|
||||
export class LoadedScriptsView extends ViewPane {
|
||||
|
||||
private treeContainer!: HTMLElement;
|
||||
private loadedScriptsItemType: IContextKey<string>;
|
||||
@@ -435,6 +435,7 @@ export class LoadedScriptsView extends BaseDebugViewPane {
|
||||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-loaded-scripts');
|
||||
dom.addClass(container, 'show-file-icons');
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
/* Expressions */
|
||||
|
||||
.monaco-workbench .debug-viewlet .monaco-list-row .expression,
|
||||
.monaco-workbench .debug-pane .monaco-list-row .expression,
|
||||
.monaco-workbench .debug-hover-widget .monaco-list-row .expression {
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
@@ -84,7 +84,7 @@
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.monaco-workbench.mac .debug-viewlet .monaco-list-row .expression,
|
||||
.monaco-workbench.mac .debug-pane .monaco-list-row .expression,
|
||||
.monaco-workbench.mac .debug-hover-widget .monaco-list-row .expression {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
user-select: text;
|
||||
-webkit-user-select: text;
|
||||
word-break: break-all;
|
||||
padding: 4px 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-hover-widget .complex-value {
|
||||
@@ -62,6 +61,7 @@
|
||||
overflow: auto;
|
||||
font-family: var(--monaco-monospace-font);
|
||||
max-height: 500px;
|
||||
padding: 4px 5px;
|
||||
}
|
||||
|
||||
.monaco-editor .debug-hover-widget .error {
|
||||
|
||||
@@ -601,7 +601,7 @@ export class RawDebugSession implements IDisposable {
|
||||
private send<R extends DebugProtocol.Response>(command: string, args: any, token?: CancellationToken, timeout?: number): Promise<R> {
|
||||
return new Promise<DebugProtocol.Response>((completeDispatch, errorDispatch) => {
|
||||
if (!this.debugAdapter) {
|
||||
errorDispatch(new Error('no debug adapter found'));
|
||||
errorDispatch(new Error(nls.localize('noDebugAdapter', "No debug adapter found. Can not send '{0}'.", command)));
|
||||
return;
|
||||
}
|
||||
let cancelationListener: IDisposable;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { equals } from 'vs/base/common/arrays';
|
||||
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -170,6 +170,7 @@ export class StartView extends ViewPane {
|
||||
}));
|
||||
attachButtonStyler(this.debugButton, this.themeService);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-start-view');
|
||||
|
||||
this.secondMessageContainer = $('.section');
|
||||
|
||||
@@ -12,12 +12,12 @@ import { IDebugService, IExpression, IScope, CONTEXT_VARIABLES_FOCUSED, IViewMod
|
||||
import { Variable, Scope } from 'vs/workbench/contrib/debug/common/debugModel';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { renderViewTree, renderVariable, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
@@ -39,7 +39,7 @@ let forgetScopes = true;
|
||||
|
||||
export const variableSetEmitter = new Emitter<void>();
|
||||
|
||||
export class VariablesView extends BaseDebugViewPane {
|
||||
export class VariablesView extends ViewPane {
|
||||
|
||||
private onFocusStackFrameScheduler: RunOnceScheduler;
|
||||
private needsRefresh = false;
|
||||
@@ -90,6 +90,7 @@ export class VariablesView extends BaseDebugViewPane {
|
||||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-variables');
|
||||
const treeContainer = renderViewTree(container);
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData, BaseDebugViewPane } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
@@ -38,7 +38,7 @@ const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024;
|
||||
let ignoreVariableSetEmitter = false;
|
||||
let useCachedEvaluation = false;
|
||||
|
||||
export class WatchExpressionsView extends BaseDebugViewPane {
|
||||
export class WatchExpressionsView extends ViewPane {
|
||||
|
||||
private onWatchExpressionsUpdatedScheduler: RunOnceScheduler;
|
||||
private needsRefresh = false;
|
||||
@@ -67,6 +67,7 @@ export class WatchExpressionsView extends BaseDebugViewPane {
|
||||
renderBody(container: HTMLElement): void {
|
||||
super.renderBody(container);
|
||||
|
||||
dom.addClass(this.element, 'debug-pane');
|
||||
dom.addClass(container, 'debug-watch');
|
||||
const treeContainer = renderViewTree(container);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } fro
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService';
|
||||
import {
|
||||
OpenExtensionsViewletAction, InstallExtensionsAction, ShowOutdatedExtensionsAction, ShowRecommendedExtensionsAction, ShowRecommendedKeymapExtensionsAction, ShowPopularExtensionsAction,
|
||||
@@ -47,6 +47,7 @@ import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewConta
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
|
||||
// Singletons
|
||||
registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService);
|
||||
@@ -290,6 +291,30 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: 'workbench.extensions.search',
|
||||
description: {
|
||||
description: localize('workbench.extensions.search.description', "Search for a specific extension"),
|
||||
args: [
|
||||
{
|
||||
name: localize('workbench.extensions.search.arg.name', "Query to use in search"),
|
||||
schema: { 'type': 'string' }
|
||||
}
|
||||
]
|
||||
},
|
||||
handler: async (accessor, query: string = '') => {
|
||||
const viewletService = accessor.get(IViewletService);
|
||||
const viewlet = await viewletService.openViewlet(VIEWLET_ID, true);
|
||||
|
||||
if (!viewlet) {
|
||||
return;
|
||||
}
|
||||
|
||||
(viewlet.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query);
|
||||
viewlet.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// File menu registration
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
|
||||
|
||||
@@ -64,7 +64,7 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa
|
||||
this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider));
|
||||
|
||||
// Set as save error handler to service for text files
|
||||
this.textFileService.saveErrorHandler = this;
|
||||
this.textFileService.files.saveErrorHandler = this;
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
@@ -676,7 +676,11 @@ export class ExplorerView extends ViewPane {
|
||||
if (item.isDisposed) {
|
||||
return this.onSelectResource(resource, reveal, retry + 1);
|
||||
}
|
||||
this.tree.reveal(item, 0.5);
|
||||
|
||||
// Don't scroll to the item if it's already visible
|
||||
if (this.tree.getRelativeTop(item) === null) {
|
||||
this.tree.reveal(item, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
this.tree.setFocus([item]);
|
||||
|
||||
@@ -29,13 +29,13 @@ export class OpenInDesktopIndicator extends Disposable implements IWorkbenchCont
|
||||
) {
|
||||
super();
|
||||
|
||||
const links = environmentService.options?.applicationLinkProvider?.();
|
||||
const links = environmentService.options?.applicationLinks;
|
||||
if (Array.isArray(links) && links?.length > 0) {
|
||||
this.installOpenInDesktopIndicator(links);
|
||||
}
|
||||
}
|
||||
|
||||
private installOpenInDesktopIndicator(links: IApplicationLink[]): void {
|
||||
private installOpenInDesktopIndicator(links: readonly IApplicationLink[]): void {
|
||||
|
||||
// Register action to trigger "Open In Desktop"
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
@@ -71,7 +71,7 @@ export class OpenInDesktopAction extends Action {
|
||||
}
|
||||
|
||||
async run(): Promise<boolean> {
|
||||
const links = this.environmentService.options?.applicationLinkProvider?.();
|
||||
const links = this.environmentService.options?.applicationLinks;
|
||||
if (Array.isArray(links)) {
|
||||
if (links.length === 1) {
|
||||
return this.openApplicationLink(links[0]);
|
||||
@@ -83,7 +83,7 @@ export class OpenInDesktopAction extends Action {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async runWithPicker(links: IApplicationLink[]): Promise<boolean> {
|
||||
private async runWithPicker(links: readonly IApplicationLink[]): Promise<boolean> {
|
||||
|
||||
// Show a picker with choices
|
||||
const quickPick = this.quickInputService.createQuickPick<IApplicationLink>();
|
||||
|
||||
@@ -27,7 +27,7 @@ import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IResourceInput, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { WorkbenchDataTree } from 'vs/platform/list/browser/listService';
|
||||
@@ -37,7 +37,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { CollapseAction } from 'vs/workbench/browser/viewlet';
|
||||
import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { OutlineConfigKeys, OutlineViewFocused, OutlineViewFiltered } from 'vs/editor/contrib/documentSymbols/outline';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { OutlineDataSource, OutlineItemComparator, OutlineSortOrder, OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItem, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree';
|
||||
@@ -49,6 +49,7 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDeco
|
||||
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
|
||||
class RequestState {
|
||||
|
||||
@@ -261,7 +262,7 @@ export class OutlinePane extends ViewPane {
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IThemeService private readonly _themeService: IThemeService,
|
||||
@IStorageService private readonly _storageService: IStorageService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@ICodeEditorService private readonly _editorService: ICodeEditorService,
|
||||
@IMarkerDecorationsService private readonly _markerDecorationService: IMarkerDecorationsService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@@ -347,7 +348,7 @@ export class OutlinePane extends ViewPane {
|
||||
|
||||
this._disposables.push(this._tree);
|
||||
this._disposables.push(this._outlineViewState.onDidChange(this._onDidChangeUserState, this));
|
||||
this._disposables.push(this.viewDescriptorService.onDidChangeLocation(({ views, from, to }) => {
|
||||
this._disposables.push(this.viewDescriptorService.onDidChangeLocation(({ views }) => {
|
||||
if (views.some(v => v.id === this.id)) {
|
||||
this._tree.updateOptions({ overrideStyles: { listBackground: this.getBackgroundColor() } });
|
||||
}
|
||||
@@ -629,15 +630,18 @@ export class OutlinePane extends ViewPane {
|
||||
}
|
||||
|
||||
private async _revealTreeSelection(model: OutlineModel, element: OutlineElement, focus: boolean, aside: boolean): Promise<void> {
|
||||
|
||||
await this._editorService.openEditor({
|
||||
resource: model.textModel.uri,
|
||||
options: {
|
||||
preserveFocus: !focus,
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
selectionRevealType: TextEditorSelectionRevealType.NearTop,
|
||||
}
|
||||
} as IResourceInput, aside ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
await this._editorService.openCodeEditor(
|
||||
{
|
||||
resource: model.textModel.uri,
|
||||
options: {
|
||||
preserveFocus: !focus,
|
||||
selection: Range.collapseToStart(element.symbol.selectionRange),
|
||||
selectionRevealType: TextEditorSelectionRevealType.NearTop,
|
||||
}
|
||||
},
|
||||
this._editorService.getActiveCodeEditor(),
|
||||
aside
|
||||
);
|
||||
}
|
||||
|
||||
private _revealEditorSelection(model: OutlineModel, selection: Selection): void {
|
||||
|
||||
@@ -29,7 +29,7 @@ import { SelectionHighlighter } from 'vs/editor/contrib/multicursor/multicursor'
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -984,7 +984,7 @@ export class DefaultPreferencesEditor extends BaseTextEditor {
|
||||
private static _getContributions(): IEditorContributionDescription[] {
|
||||
const skipContributions = [FoldingController.ID, SelectionHighlighter.ID, FindController.ID];
|
||||
const contributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1);
|
||||
contributions.push({ id: DefaultSettingsEditorContribution.ID, ctor: DefaultSettingsEditorContribution });
|
||||
contributions.push({ id: DefaultSettingsEditorContribution.ID, ctor: DefaultSettingsEditorContribution as IConstructorSignature1<ICodeEditor, editorCommon.IEditorContribution> });
|
||||
return contributions;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { isMacintosh, isNative } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isNative, isLinux } from 'vs/base/common/platform';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
@@ -26,6 +26,7 @@ interface IConfiguration extends IWindowsConfiguration {
|
||||
telemetry: { enableCrashReporter: boolean };
|
||||
workbench: { list: { horizontalScrolling: boolean } };
|
||||
debug: { console: { wordWrap: boolean } };
|
||||
editor: { accessibilitySupport: 'on' | 'off' | 'auto' };
|
||||
}
|
||||
|
||||
export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution {
|
||||
@@ -38,6 +39,7 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
|
||||
private enableCrashReporter: boolean | undefined;
|
||||
private treeHorizontalScrolling: boolean | undefined;
|
||||
private debugConsoleWordWrap: boolean | undefined;
|
||||
private accessibilitySupport: 'on' | 'off' | 'auto' | undefined;
|
||||
|
||||
constructor(
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@@ -103,6 +105,14 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
|
||||
this.enableCrashReporter = config.telemetry.enableCrashReporter;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// On linux turning on accessibility support will also pass this flag to the chrome renderer, thus a restart is required
|
||||
if (isLinux && typeof config.editor?.accessibilitySupport === 'string' && config.editor.accessibilitySupport !== this.accessibilitySupport) {
|
||||
this.accessibilitySupport = config.editor.accessibilitySupport;
|
||||
if (this.accessibilitySupport === 'on') {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Notify only when changed and we are the focused window (avoids notification spam across windows)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user