Merge branch 'main' into joao/esrp-errors
This commit is contained in:
commit
d390635c56
|
@ -9,6 +9,7 @@ const es = require("event-stream");
|
|||
const vfs = require("vinyl-fs");
|
||||
const util = require("../lib/util");
|
||||
const merge = require("gulp-merge-json");
|
||||
const gzip = require("gulp-gzip");
|
||||
const azure = require('gulp-azure-storage');
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
const commit = util.getVersion(root);
|
||||
|
@ -16,6 +17,7 @@ function main() {
|
|||
return es.merge(vfs.src('out-vscode-min/nls.metadata.json', { base: 'out-vscode-min' }), vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' }))
|
||||
.pipe(merge({
|
||||
fileName: 'combined.nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
edit: (parsedJson, file) => {
|
||||
let key;
|
||||
if (file.base === 'out-vscode-min') {
|
||||
|
@ -64,6 +66,7 @@ function main() {
|
|||
return { [key]: parsedJson };
|
||||
},
|
||||
}))
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(vfs.dest('./nlsMetadata'))
|
||||
.pipe(es.through(function (data) {
|
||||
console.log(`Uploading ${data.path}`);
|
||||
|
@ -75,7 +78,11 @@ function main() {
|
|||
account: process.env.AZURE_STORAGE_ACCOUNT,
|
||||
key: process.env.AZURE_STORAGE_ACCESS_KEY,
|
||||
container: 'nlsmetadata',
|
||||
prefix: commit + '/'
|
||||
prefix: commit + '/',
|
||||
contentSettings: {
|
||||
contentEncoding: 'gzip',
|
||||
cacheControl: 'max-age=31536000, public'
|
||||
}
|
||||
}));
|
||||
}
|
||||
main();
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as Vinyl from 'vinyl';
|
|||
import * as vfs from 'vinyl-fs';
|
||||
import * as util from '../lib/util';
|
||||
import * as merge from 'gulp-merge-json';
|
||||
import * as gzip from 'gulp-gzip';
|
||||
const azure = require('gulp-azure-storage');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
@ -30,6 +31,7 @@ function main() {
|
|||
vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' }))
|
||||
.pipe(merge({
|
||||
fileName: 'combined.nls.metadata.json',
|
||||
jsonSpace: '',
|
||||
edit: (parsedJson, file) => {
|
||||
let key;
|
||||
if (file.base === 'out-vscode-min') {
|
||||
|
@ -82,6 +84,7 @@ function main() {
|
|||
return { [key]: parsedJson };
|
||||
},
|
||||
}))
|
||||
.pipe(gzip({ append: false }))
|
||||
.pipe(vfs.dest('./nlsMetadata'))
|
||||
.pipe(es.through(function (data: Vinyl) {
|
||||
console.log(`Uploading ${data.path}`);
|
||||
|
@ -93,7 +96,11 @@ function main() {
|
|||
account: process.env.AZURE_STORAGE_ACCOUNT,
|
||||
key: process.env.AZURE_STORAGE_ACCESS_KEY,
|
||||
container: 'nlsmetadata',
|
||||
prefix: commit + '/'
|
||||
prefix: commit + '/',
|
||||
contentSettings: {
|
||||
contentEncoding: 'gzip',
|
||||
cacheControl: 'max-age=31536000, public'
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
@ -48,13 +48,6 @@ export class GitFileSystemProvider implements FileSystemProvider {
|
|||
model.onDidChangeRepository(this.onDidChangeRepository, this),
|
||||
model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this),
|
||||
workspace.registerFileSystemProvider('git', this, { isReadonly: true, isCaseSensitive: true }),
|
||||
workspace.registerResourceLabelFormatter({
|
||||
scheme: 'git',
|
||||
formatting: {
|
||||
label: '${path} (git)',
|
||||
separator: '/'
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
setInterval(() => this.cleanup(), FIVE_MINUTES);
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { window, workspace, Disposable, TextDocumentContentChangeEvent, TextDocument, Position, SnippetString } from 'vscode';
|
||||
import { window, workspace, Disposable, TextDocument, Position, SnippetString, TextDocumentChangeEvent, TextDocumentChangeReason } from 'vscode';
|
||||
import { Runtime } from './htmlClient';
|
||||
|
||||
export function activateTagClosing(tagProvider: (document: TextDocument, position: Position) => Thenable<string>, supportedLanguages: { [id: string]: boolean }, configName: string, runtime: Runtime): Disposable {
|
||||
|
||||
let disposables: Disposable[] = [];
|
||||
workspace.onDidChangeTextDocument(event => onDidChangeTextDocument(event.document, event.contentChanges), null, disposables);
|
||||
const disposables: Disposable[] = [];
|
||||
workspace.onDidChangeTextDocument(onDidChangeTextDocument, null, disposables);
|
||||
|
||||
let isEnabled = false;
|
||||
updateEnabledState();
|
||||
|
@ -25,11 +25,11 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio
|
|||
|
||||
function updateEnabledState() {
|
||||
isEnabled = false;
|
||||
let editor = window.activeTextEditor;
|
||||
const editor = window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
let document = editor.document;
|
||||
const document = editor.document;
|
||||
if (!supportedLanguages[document.languageId]) {
|
||||
return;
|
||||
}
|
||||
|
@ -39,33 +39,34 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio
|
|||
isEnabled = true;
|
||||
}
|
||||
|
||||
function onDidChangeTextDocument(document: TextDocument, changes: readonly TextDocumentContentChangeEvent[]) {
|
||||
if (!isEnabled) {
|
||||
function onDidChangeTextDocument({ document, contentChanges, reason }: TextDocumentChangeEvent) {
|
||||
if (!isEnabled || contentChanges.length === 0 || reason === TextDocumentChangeReason.Undo) {
|
||||
return;
|
||||
}
|
||||
let activeDocument = window.activeTextEditor && window.activeTextEditor.document;
|
||||
if (document !== activeDocument || changes.length === 0) {
|
||||
const activeDocument = window.activeTextEditor && window.activeTextEditor.document;
|
||||
if (document !== activeDocument) {
|
||||
return;
|
||||
}
|
||||
if (timeout) {
|
||||
timeout.dispose();
|
||||
}
|
||||
let lastChange = changes[changes.length - 1];
|
||||
let lastCharacter = lastChange.text[lastChange.text.length - 1];
|
||||
|
||||
const lastChange = contentChanges[contentChanges.length - 1];
|
||||
const lastCharacter = lastChange.text[lastChange.text.length - 1];
|
||||
if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') {
|
||||
return;
|
||||
}
|
||||
let rangeStart = lastChange.range.start;
|
||||
let version = document.version;
|
||||
const rangeStart = lastChange.range.start;
|
||||
const version = document.version;
|
||||
timeout = runtime.timer.setTimeout(() => {
|
||||
let position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
|
||||
const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length);
|
||||
tagProvider(document, position).then(text => {
|
||||
if (text && isEnabled) {
|
||||
let activeEditor = window.activeTextEditor;
|
||||
const activeEditor = window.activeTextEditor;
|
||||
if (activeEditor) {
|
||||
let activeDocument = activeEditor.document;
|
||||
const activeDocument = activeEditor.document;
|
||||
if (document === activeDocument && activeDocument.version === version) {
|
||||
let selections = activeEditor.selections;
|
||||
const selections = activeEditor.selections;
|
||||
if (selections.length && selections.some(s => s.active.isEqual(position))) {
|
||||
activeEditor.insertSnippet(new SnippetString(text), selections.map(s => s.active));
|
||||
} else {
|
||||
|
|
|
@ -22,7 +22,7 @@ export class CapabilitiesStatus extends Disposable {
|
|||
|
||||
this._statusItem = this._register(vscode.languages.createLanguageStatusItem('typescript.capabilities', jsTsLanguageModes));
|
||||
|
||||
this._statusItem.name = localize('capabilitiesStatus.name', "IntelliSense IntelliSense Status");
|
||||
this._statusItem.name = localize('capabilitiesStatus.name', "IntelliSense Status");
|
||||
|
||||
this._register(this._client.onTsServerStarted(() => this.update()));
|
||||
this._register(this._client.onDidChangeCapabilities(() => this.update()));
|
||||
|
|
|
@ -230,7 +230,7 @@ import { assertNoRpc } from '../utils';
|
|||
|
||||
test('onDidChangeTerminalState should fire after writing to a terminal', async () => {
|
||||
const terminal = window.createTerminal();
|
||||
deepStrictEqual(terminal.state, { interactedWith: false });
|
||||
deepStrictEqual(terminal.state, { isInteractedWith: false });
|
||||
const eventState = await new Promise<TerminalState>(r => {
|
||||
disposables.push(window.onDidChangeTerminalState(e => {
|
||||
if (e === terminal) {
|
||||
|
@ -239,8 +239,8 @@ import { assertNoRpc } from '../utils';
|
|||
}));
|
||||
terminal.sendText('test');
|
||||
});
|
||||
deepStrictEqual(eventState, { interactedWith: true });
|
||||
deepStrictEqual(terminal.state, { interactedWith: true });
|
||||
deepStrictEqual(eventState, { isInteractedWith: true });
|
||||
deepStrictEqual(terminal.state, { isInteractedWith: true });
|
||||
await new Promise<void>(r => {
|
||||
disposables.push(window.onDidCloseTerminal(t => {
|
||||
if (t === terminal) {
|
||||
|
|
|
@ -349,24 +349,17 @@ suite('vscode API - window', () => {
|
|||
|
||||
//#region Tabs API tests
|
||||
test('Tabs - Ensure tabs getter is correct', async () => {
|
||||
const docA = await workspace.openTextDocument(await createRandomFile());
|
||||
const docB = await workspace.openTextDocument(await createRandomFile());
|
||||
const docC = await workspace.openTextDocument(await createRandomFile());
|
||||
// Add back actual notebook doc once stuck promise is figured out
|
||||
//const notebookDoc = await workspace.openNotebookDocument(await createRandomFile('', undefined, '.vsctestnb'));
|
||||
const notebookDoc = await workspace.openTextDocument(await createRandomFile());
|
||||
// const [docA, docB, docC, notebookDoc] = await Promise.all([
|
||||
// workspace.openTextDocument(await createRandomFile()),
|
||||
// workspace.openTextDocument(await createRandomFile()),
|
||||
// workspace.openTextDocument(await createRandomFile()),
|
||||
// workspace.openNotebookDocument(await createRandomFile('', undefined, '.vsctestnb'))
|
||||
// ]);
|
||||
const [docA, docB, docC, notebookDoc] = await Promise.all([
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openTextDocument(await createRandomFile()),
|
||||
workspace.openNotebookDocument('jupyter-notebook', undefined)
|
||||
]);
|
||||
|
||||
await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false });
|
||||
await window.showTextDocument(docB, { viewColumn: ViewColumn.Two, preview: false });
|
||||
await window.showTextDocument(docC, { viewColumn: ViewColumn.Three, preview: false });
|
||||
await window.showTextDocument(notebookDoc, { viewColumn: ViewColumn.One, preview: false });
|
||||
//await window.showNotebookDocument(notebookDoc, { viewColumn: ViewColumn.One, preview: false });
|
||||
await window.showNotebookDocument(notebookDoc, { viewColumn: ViewColumn.One, preview: false });
|
||||
|
||||
const leftDiff = await createRandomFile();
|
||||
const rightDiff = await createRandomFile();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.61.0",
|
||||
"distro": "7f4d861ce33b303be575006848803ad7a523c029",
|
||||
"distro": "c464d76c12ba556d77206699e13ce63655e14222",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
|
|
@ -1367,7 +1367,7 @@ export function detectFullscreen(): IDetectedFullscreen | null {
|
|||
export function safeInnerHtml(node: HTMLElement, value: string): void {
|
||||
const options: dompurify.Config = {
|
||||
ALLOWED_TAGS: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
|
||||
ALLOWED_ATTR: ['href', 'data-href', 'data-command', 'target', 'title', 'name', 'src', 'alt', 'class', 'id', 'role', 'tabindex', 'style', 'data-code', 'width', 'height', 'align', 'x-dispatch', 'required', 'checked', 'placeholder'],
|
||||
ALLOWED_ATTR: ['href', 'data-href', 'data-command', 'target', 'title', 'name', 'src', 'alt', 'class', 'id', 'role', 'tabindex', 'style', 'data-code', 'width', 'height', 'align', 'x-dispatch', 'required', 'checked', 'placeholder', 'type'],
|
||||
RETURN_DOM: false,
|
||||
RETURN_DOM_FRAGMENT: false,
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@ export class BaseDropdown extends ActionRunner {
|
|||
private contents?: HTMLElement;
|
||||
|
||||
private visible: boolean | undefined;
|
||||
private _onDidChangeVisibility = new Emitter<boolean>();
|
||||
private _onDidChangeVisibility = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
|
||||
|
||||
constructor(container: HTMLElement, options: IBaseDropdownOptions) {
|
||||
|
|
|
@ -31,6 +31,7 @@ export interface IFindInputOptions extends IFindInputStyles {
|
|||
readonly appendWholeWordsLabel?: string;
|
||||
readonly appendRegexLabel?: string;
|
||||
readonly history?: string[];
|
||||
readonly showHistoryHint?: () => boolean;
|
||||
}
|
||||
|
||||
export interface IFindInputStyles extends IInputBoxStyles {
|
||||
|
@ -150,6 +151,7 @@ export class FindInput extends Widget {
|
|||
inputValidationErrorForeground: this.inputValidationErrorForeground,
|
||||
inputValidationErrorBorder: this.inputValidationErrorBorder,
|
||||
history,
|
||||
showHistoryHint: options.showHistoryHint,
|
||||
flexibleHeight,
|
||||
flexibleWidth,
|
||||
flexibleMaxHeight
|
||||
|
|
|
@ -30,6 +30,7 @@ export interface IReplaceInputOptions extends IReplaceInputStyles {
|
|||
|
||||
readonly appendPreserveCaseLabel?: string;
|
||||
readonly history?: string[];
|
||||
readonly showHistoryHint?: () => boolean;
|
||||
}
|
||||
|
||||
export interface IReplaceInputStyles extends IInputBoxStyles {
|
||||
|
@ -157,6 +158,7 @@ export class ReplaceInput extends Widget {
|
|||
inputValidationErrorForeground: this.inputValidationErrorForeground,
|
||||
inputValidationErrorBorder: this.inputValidationErrorBorder,
|
||||
history,
|
||||
showHistoryHint: options.showHistoryHint,
|
||||
flexibleHeight,
|
||||
flexibleWidth,
|
||||
flexibleMaxHeight
|
||||
|
|
|
@ -9,7 +9,7 @@ import { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget, IHoverWidg
|
|||
import { IIconLabelMarkdownString } from 'vs/base/browser/ui/iconLabel/iconLabel';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { IMarkdownString, isMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isFunction, isString } from 'vs/base/common/types';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -93,9 +93,9 @@ class UpdatableHoverWidget implements IDisposable {
|
|||
private show(content: ResolvedMarkdownTooltipContent): void {
|
||||
const oldHoverWidget = this._hoverWidget;
|
||||
|
||||
if (content) {
|
||||
if (this.hasContent(content)) {
|
||||
const hoverOptions: IHoverDelegateOptions = {
|
||||
content: content,
|
||||
content,
|
||||
target: this.target,
|
||||
showPointer: this.hoverDelegate.placement === 'element',
|
||||
hoverPosition: HoverPosition.BELOW,
|
||||
|
@ -107,6 +107,18 @@ class UpdatableHoverWidget implements IDisposable {
|
|||
oldHoverWidget?.dispose();
|
||||
}
|
||||
|
||||
private hasContent(content: ResolvedMarkdownTooltipContent): content is NonNullable<ResolvedMarkdownTooltipContent> {
|
||||
if (!content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isMarkdownString(content)) {
|
||||
return this.hasContent(content.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
get isDisposed() {
|
||||
return this._hoverWidget?.isDisposed;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
opacity: 0.75;
|
||||
font-size: 90%;
|
||||
font-weight: 600;
|
||||
margin: 0 16px 0 5px;
|
||||
margin: auto 16px 0 5px; /* https://github.com/microsoft/vscode/issues/113223 */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,11 +92,11 @@ const defaultOpts = {
|
|||
export class InputBox extends Widget {
|
||||
private contextViewProvider?: IContextViewProvider;
|
||||
element: HTMLElement;
|
||||
private input: HTMLInputElement;
|
||||
protected input: HTMLInputElement;
|
||||
private actionbar?: ActionBar;
|
||||
private options: IInputOptions;
|
||||
private message: IMessage | null;
|
||||
private placeholder: string;
|
||||
protected placeholder: string;
|
||||
private tooltip: string;
|
||||
private ariaLabel: string;
|
||||
private validation?: IInputValidator;
|
||||
|
@ -634,15 +634,75 @@ export class InputBox extends Widget {
|
|||
|
||||
export interface IHistoryInputOptions extends IInputOptions {
|
||||
history: string[];
|
||||
readonly showHistoryHint?: () => boolean;
|
||||
}
|
||||
|
||||
export class HistoryInputBox extends InputBox implements IHistoryNavigationWidget {
|
||||
|
||||
private readonly history: HistoryNavigator<string>;
|
||||
private observer: MutationObserver | undefined;
|
||||
|
||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider | undefined, options: IHistoryInputOptions) {
|
||||
const NLS_PLACEHOLDER_HISTORY_HINT = nls.localize({ key: 'history.inputbox.hint', comment: ['Text will be prefixed with \u21C5 plus a single space, then used as a hint where input field keeps history'] }, "for history");
|
||||
const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX = ` or \u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT}`;
|
||||
const NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS = ` (\u21C5 ${NLS_PLACEHOLDER_HISTORY_HINT})`;
|
||||
super(container, contextViewProvider, options);
|
||||
this.history = new HistoryNavigator<string>(options.history, 100);
|
||||
|
||||
// Function to append the history suffix to the placeholder if necessary
|
||||
const addSuffix = () => {
|
||||
if (options.showHistoryHint && options.showHistoryHint() && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX) && !this.placeholder.endsWith(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS) && this.history.getHistory().length) {
|
||||
const suffix = this.placeholder.endsWith(')') ? NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX : NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS;
|
||||
const suffixedPlaceholder = this.placeholder + suffix;
|
||||
if (options.showPlaceholderOnFocus && document.activeElement !== this.input) {
|
||||
this.placeholder = suffixedPlaceholder;
|
||||
}
|
||||
else {
|
||||
this.setPlaceHolder(suffixedPlaceholder);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Spot the change to the textarea class attribute which occurs when it changes between non-empty and empty,
|
||||
// and add the history suffix to the placeholder if not yet present
|
||||
this.observer = new MutationObserver((mutationList: MutationRecord[], observer: MutationObserver) => {
|
||||
mutationList.forEach((mutation: MutationRecord) => {
|
||||
if (!mutation.target.textContent) {
|
||||
addSuffix();
|
||||
}
|
||||
});
|
||||
});
|
||||
this.observer.observe(this.input, { attributeFilter: ['class'] });
|
||||
|
||||
this.onfocus(this.input, () => addSuffix());
|
||||
this.onblur(this.input, () => {
|
||||
const resetPlaceholder = (historyHint: string) => {
|
||||
if (!this.placeholder.endsWith(historyHint)) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
const revertedPlaceholder = this.placeholder.slice(0, this.placeholder.length - historyHint.length);
|
||||
if (options.showPlaceholderOnFocus) {
|
||||
this.placeholder = revertedPlaceholder;
|
||||
}
|
||||
else {
|
||||
this.setPlaceHolder(revertedPlaceholder);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
if (!resetPlaceholder(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX_IN_PARENS)) {
|
||||
resetPlaceholder(NLS_PLACEHOLDER_HISTORY_HINT_SUFFIX);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
override dispose() {
|
||||
super.dispose();
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
this.observer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public addToHistory(): void {
|
||||
|
|
|
@ -407,6 +407,7 @@ function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, o
|
|||
}
|
||||
|
||||
// Flush contents (not metadata) of the file to disk
|
||||
// https://github.com/microsoft/vscode/issues/9589
|
||||
fs.fdatasync(fd, (syncError: Error | null) => {
|
||||
|
||||
// In some exotic setups it is well possible that node fails to sync
|
||||
|
@ -444,7 +445,7 @@ export function writeFileSync(path: string, data: string | Buffer, options?: IWr
|
|||
|
||||
// Flush contents (not metadata) of the file to disk
|
||||
try {
|
||||
fs.fdatasyncSync(fd);
|
||||
fs.fdatasyncSync(fd); // https://github.com/microsoft/vscode/issues/9589
|
||||
} catch (syncError) {
|
||||
console.warn('[node.js fs] fdatasyncSync is now disabled for this session because it failed: ', syncError);
|
||||
canFlush = false;
|
||||
|
|
|
@ -279,8 +279,7 @@ class SharedProcessMain extends Disposable {
|
|||
new PtyHostService({
|
||||
graceTime: LocalReconnectConstants.GraceTime,
|
||||
shortGraceTime: LocalReconnectConstants.ShortGraceTime,
|
||||
scrollback: configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100,
|
||||
useExperimentalSerialization: configurationService.getValue<boolean>(TerminalSettingId.PersistentSessionExperimentalSerializer) ?? true,
|
||||
scrollback: configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
|
||||
},
|
||||
configurationService,
|
||||
logService,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { tail } from 'vs/base/common/arrays';
|
||||
import { DenseKeyProvider, SmallImmutableSet } from './smallImmutableSet';
|
||||
import { SmallImmutableSet } from './smallImmutableSet';
|
||||
import { lengthAdd, lengthZero, Length, lengthHash } from './length';
|
||||
|
||||
export const enum AstNodeKind {
|
||||
|
@ -20,7 +20,7 @@ export type AstNode = PairAstNode | ListAstNode | BracketAstNode | InvalidBracke
|
|||
abstract class BaseAstNode {
|
||||
abstract readonly kind: AstNodeKind;
|
||||
abstract readonly children: readonly AstNode[];
|
||||
abstract readonly unopenedBrackets: SmallImmutableSet<number>;
|
||||
abstract readonly missingBracketIds: SmallImmutableSet<number>;
|
||||
|
||||
/**
|
||||
* In case of a list, determines the height of the (2,3) tree.
|
||||
|
@ -55,7 +55,6 @@ abstract class BaseAstNode {
|
|||
|
||||
export class PairAstNode extends BaseAstNode {
|
||||
public static create(
|
||||
category: number,
|
||||
openingBracket: BracketAstNode,
|
||||
child: AstNode | null,
|
||||
closingBracket: BracketAstNode | null
|
||||
|
@ -71,7 +70,7 @@ export class PairAstNode extends BaseAstNode {
|
|||
children.push(closingBracket);
|
||||
}
|
||||
|
||||
return new PairAstNode(length, category, children, child ? child.unopenedBrackets : SmallImmutableSet.getEmpty());
|
||||
return new PairAstNode(length, children, child ? child.missingBracketIds : SmallImmutableSet.getEmpty());
|
||||
}
|
||||
|
||||
get kind(): AstNodeKind.Pair {
|
||||
|
@ -82,7 +81,7 @@ export class PairAstNode extends BaseAstNode {
|
|||
}
|
||||
|
||||
canBeReused(
|
||||
expectedClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<number>,
|
||||
endLineDidChange: boolean
|
||||
) {
|
||||
if (this.closingBracket === null) {
|
||||
|
@ -96,7 +95,7 @@ export class PairAstNode extends BaseAstNode {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (expectedClosingCategories.intersects(this.unopenedBrackets)) {
|
||||
if (openedBracketIds.intersects(this.missingBracketIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -105,7 +104,6 @@ export class PairAstNode extends BaseAstNode {
|
|||
|
||||
flattenLists(): PairAstNode {
|
||||
return PairAstNode.create(
|
||||
this.category,
|
||||
this.openingBracket.flattenLists(),
|
||||
this.child && this.child.flattenLists(),
|
||||
this.closingBracket && this.closingBracket.flattenLists()
|
||||
|
@ -138,9 +136,8 @@ export class PairAstNode extends BaseAstNode {
|
|||
|
||||
private constructor(
|
||||
length: Length,
|
||||
public readonly category: number,
|
||||
public readonly children: readonly AstNode[],
|
||||
public readonly unopenedBrackets: SmallImmutableSet<number>
|
||||
public readonly missingBracketIds: SmallImmutableSet<number>
|
||||
) {
|
||||
super(length);
|
||||
}
|
||||
|
@ -148,9 +145,8 @@ export class PairAstNode extends BaseAstNode {
|
|||
clone(): PairAstNode {
|
||||
return new PairAstNode(
|
||||
this.length,
|
||||
this.category,
|
||||
clone(this.children),
|
||||
this.unopenedBrackets
|
||||
this.missingBracketIds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -172,10 +168,10 @@ export class ListAstNode extends BaseAstNode {
|
|||
return new ListAstNode(lengthZero, 0, items, SmallImmutableSet.getEmpty());
|
||||
} else {
|
||||
let length = items[0].length;
|
||||
let unopenedBrackets = items[0].unopenedBrackets;
|
||||
let unopenedBrackets = items[0].missingBracketIds;
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
length = lengthAdd(length, items[i].length);
|
||||
unopenedBrackets = unopenedBrackets.merge(items[i].unopenedBrackets);
|
||||
unopenedBrackets = unopenedBrackets.merge(items[i].missingBracketIds);
|
||||
}
|
||||
return new ListAstNode(length, items[0].listHeight + 1, items, unopenedBrackets);
|
||||
}
|
||||
|
@ -187,7 +183,7 @@ export class ListAstNode extends BaseAstNode {
|
|||
get children(): readonly AstNode[] {
|
||||
return this._items;
|
||||
}
|
||||
get unopenedBrackets(): SmallImmutableSet<number> {
|
||||
get missingBracketIds(): SmallImmutableSet<number> {
|
||||
return this._unopenedBrackets;
|
||||
}
|
||||
|
||||
|
@ -201,7 +197,7 @@ export class ListAstNode extends BaseAstNode {
|
|||
}
|
||||
|
||||
canBeReused(
|
||||
expectedClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<number>,
|
||||
endLineDidChange: boolean
|
||||
): boolean {
|
||||
if (this._items.length === 0) {
|
||||
|
@ -209,7 +205,7 @@ export class ListAstNode extends BaseAstNode {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (expectedClosingCategories.intersects(this.unopenedBrackets)) {
|
||||
if (openedBracketIds.intersects(this.missingBracketIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -219,7 +215,7 @@ export class ListAstNode extends BaseAstNode {
|
|||
}
|
||||
|
||||
return lastChild.canBeReused(
|
||||
expectedClosingCategories,
|
||||
openedBracketIds,
|
||||
endLineDidChange
|
||||
);
|
||||
}
|
||||
|
@ -238,7 +234,7 @@ export class ListAstNode extends BaseAstNode {
|
|||
}
|
||||
|
||||
clone(): ListAstNode {
|
||||
return new ListAstNode(this.length, this.listHeight, clone(this._items), this.unopenedBrackets);
|
||||
return new ListAstNode(this.length, this.listHeight, clone(this._items), this.missingBracketIds);
|
||||
}
|
||||
|
||||
private handleChildrenChanged(): void {
|
||||
|
@ -248,10 +244,10 @@ export class ListAstNode extends BaseAstNode {
|
|||
}
|
||||
|
||||
let length = items[0].length;
|
||||
let unopenedBrackets = items[0].unopenedBrackets;
|
||||
let unopenedBrackets = items[0].missingBracketIds;
|
||||
for (let i = 1; i < items.length; i++) {
|
||||
length = lengthAdd(length, items[i].length);
|
||||
unopenedBrackets = unopenedBrackets.merge(items[i].unopenedBrackets);
|
||||
unopenedBrackets = unopenedBrackets.merge(items[i].missingBracketIds);
|
||||
}
|
||||
this._length = length;
|
||||
this._unopenedBrackets = unopenedBrackets;
|
||||
|
@ -372,12 +368,12 @@ export class TextAstNode extends BaseAstNode {
|
|||
get children(): readonly AstNode[] {
|
||||
return emptyArray;
|
||||
}
|
||||
get unopenedBrackets(): SmallImmutableSet<number> {
|
||||
get missingBracketIds(): SmallImmutableSet<number> {
|
||||
return SmallImmutableSet.getEmpty();
|
||||
}
|
||||
|
||||
canBeReused(
|
||||
expectedClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<number>,
|
||||
endLineDidChange: boolean
|
||||
) {
|
||||
// Don't reuse text from a line that got changed.
|
||||
|
@ -422,7 +418,7 @@ export class BracketAstNode extends BaseAstNode {
|
|||
return emptyArray;
|
||||
}
|
||||
|
||||
get unopenedBrackets(): SmallImmutableSet<number> {
|
||||
get missingBracketIds(): SmallImmutableSet<number> {
|
||||
return SmallImmutableSet.getEmpty();
|
||||
}
|
||||
|
||||
|
@ -456,18 +452,18 @@ export class InvalidBracketAstNode extends BaseAstNode {
|
|||
return emptyArray;
|
||||
}
|
||||
|
||||
public readonly unopenedBrackets: SmallImmutableSet<number>;
|
||||
public readonly missingBracketIds: SmallImmutableSet<number>;
|
||||
|
||||
constructor(category: number, length: Length, denseKeyProvider: DenseKeyProvider<number>) {
|
||||
constructor(closingBrackets: SmallImmutableSet<number>, length: Length) {
|
||||
super(length);
|
||||
this.unopenedBrackets = SmallImmutableSet.getEmpty().add(category, denseKeyProvider);
|
||||
this.missingBracketIds = closingBrackets;
|
||||
}
|
||||
|
||||
canBeReused(
|
||||
expectedClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<number>,
|
||||
endLineDidChange: boolean
|
||||
) {
|
||||
return !expectedClosingCategories.intersects(this.unopenedBrackets);
|
||||
return !openedBracketIds.intersects(this.missingBracketIds);
|
||||
}
|
||||
|
||||
flattenLists(): InvalidBracketAstNode {
|
||||
|
|
|
@ -120,8 +120,8 @@ class BracketPairColorizerImpl extends Disposable implements DecorationProvider
|
|||
private initialAstWithoutTokens: AstNode | undefined;
|
||||
private astWithTokens: AstNode | undefined;
|
||||
|
||||
private readonly brackets = new LanguageAgnosticBracketTokens([]);
|
||||
private readonly denseKeyProvider = new DenseKeyProvider<number>();
|
||||
private readonly denseKeyProvider = new DenseKeyProvider<string>();
|
||||
private readonly brackets = new LanguageAgnosticBracketTokens(this.denseKeyProvider);
|
||||
|
||||
public didLanguageChange(languageId: LanguageId): boolean {
|
||||
return this.brackets.didLanguageChange(languageId);
|
||||
|
@ -159,7 +159,7 @@ class BracketPairColorizerImpl extends Disposable implements DecorationProvider
|
|||
// There are no token information yet
|
||||
const brackets = this.brackets.getSingleLanguageBracketTokens(this.textModel.getLanguageIdentifier().id);
|
||||
const tokenizer = new FastTokenizer(this.textModel.getValue(), brackets);
|
||||
this.initialAstWithoutTokens = parseDocument(tokenizer, [], undefined, this.denseKeyProvider);
|
||||
this.initialAstWithoutTokens = parseDocument(tokenizer, [], undefined);
|
||||
this.astWithTokens = this.initialAstWithoutTokens.clone();
|
||||
} else if (textModel.backgroundTokenizationState === BackgroundTokenizationState.Completed) {
|
||||
// Skip the initial ast, as there is no flickering.
|
||||
|
@ -196,7 +196,7 @@ class BracketPairColorizerImpl extends Disposable implements DecorationProvider
|
|||
const isPure = false;
|
||||
const previousAstClone = isPure ? previousAst?.clone() : previousAst;
|
||||
const tokenizer = new TextBufferTokenizer(this.textModel, this.brackets);
|
||||
const result = parseDocument(tokenizer, edits, previousAstClone, this.denseKeyProvider);
|
||||
const result = parseDocument(tokenizer, edits, previousAstClone);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,51 +4,70 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { escapeRegExpCharacters } from 'vs/base/common/strings';
|
||||
import { toLength } from 'vs/editor/common/model/bracketPairColorizer/length';
|
||||
import { SmallImmutableSet, DenseKeyProvider, identityKeyProvider } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
|
||||
import { LanguageId } from 'vs/editor/common/modes';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { BracketAstNode } from './ast';
|
||||
import { toLength } from './length';
|
||||
import { Token, TokenKind } from './tokenizer';
|
||||
import { OpeningBracketId, Token, TokenKind } from './tokenizer';
|
||||
|
||||
export class BracketTokens {
|
||||
static createFromLanguage(languageId: LanguageId, customBracketPairs: readonly [string, string][]): BracketTokens {
|
||||
static createFromLanguage(languageId: LanguageId, denseKeyProvider: DenseKeyProvider<string>): BracketTokens {
|
||||
function getId(languageId: LanguageId, openingText: string): OpeningBracketId {
|
||||
return denseKeyProvider.getKey(`${languageId}:::${openingText}`);
|
||||
}
|
||||
|
||||
const brackets = [...(LanguageConfigurationRegistry.getColorizedBracketPairs(languageId))];
|
||||
|
||||
const tokens = new BracketTokens();
|
||||
const closingBrackets = new Map</* closingText */ string, { openingBrackets: SmallImmutableSet<OpeningBracketId>, first: OpeningBracketId }>();
|
||||
const openingBrackets = new Set</* openingText */ string>();
|
||||
|
||||
let idxOffset = 0;
|
||||
for (const pair of brackets) {
|
||||
tokens.addBracket(languageId, pair[0], TokenKind.OpeningBracket, idxOffset);
|
||||
tokens.addBracket(languageId, pair[1], TokenKind.ClosingBracket, idxOffset);
|
||||
idxOffset++;
|
||||
for (const [openingText, closingText] of brackets) {
|
||||
openingBrackets.add(openingText);
|
||||
|
||||
let info = closingBrackets.get(closingText);
|
||||
const openingTextId = getId(languageId, openingText);
|
||||
if (!info) {
|
||||
info = { openingBrackets: SmallImmutableSet.getEmpty(), first: openingTextId };
|
||||
closingBrackets.set(closingText, info);
|
||||
}
|
||||
info.openingBrackets = info.openingBrackets.add(openingTextId, identityKeyProvider);
|
||||
}
|
||||
|
||||
for (const pair of customBracketPairs) {
|
||||
idxOffset++;
|
||||
tokens.addBracket(languageId, pair[0], TokenKind.OpeningBracket, idxOffset);
|
||||
tokens.addBracket(languageId, pair[1], TokenKind.ClosingBracket, idxOffset);
|
||||
const map = new Map<string, Token>();
|
||||
|
||||
for (const [closingText, info] of closingBrackets) {
|
||||
const length = toLength(0, closingText.length);
|
||||
map.set(closingText, new Token(
|
||||
length,
|
||||
TokenKind.ClosingBracket,
|
||||
info.first,
|
||||
info.openingBrackets,
|
||||
BracketAstNode.create(length)
|
||||
));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
for (const openingText of openingBrackets) {
|
||||
const length = toLength(0, openingText.length);
|
||||
const openingTextId = getId(languageId, openingText);
|
||||
map.set(openingText, new Token(
|
||||
length,
|
||||
TokenKind.OpeningBracket,
|
||||
openingTextId,
|
||||
SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider),
|
||||
BracketAstNode.create(length)
|
||||
));
|
||||
}
|
||||
|
||||
return new BracketTokens(map);
|
||||
}
|
||||
|
||||
private hasRegExp = false;
|
||||
private _regExpGlobal: RegExp | null = null;
|
||||
private readonly map = new Map<string, Token>();
|
||||
|
||||
private addBracket(languageId: LanguageId, value: string, kind: TokenKind, idx: number): void {
|
||||
const length = toLength(0, value.length);
|
||||
this.map.set(value,
|
||||
new Token(
|
||||
length,
|
||||
kind,
|
||||
// A language can have at most 1000 bracket pairs.
|
||||
languageId * 1000 + idx,
|
||||
languageId,
|
||||
BracketAstNode.create(length)
|
||||
)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly map: Map<string, Token>
|
||||
) { }
|
||||
|
||||
getRegExpStr(): string | null {
|
||||
if (this.isEmpty) {
|
||||
|
@ -85,7 +104,7 @@ export class BracketTokens {
|
|||
export class LanguageAgnosticBracketTokens {
|
||||
private readonly languageIdToBracketTokens: Map<LanguageId, BracketTokens> = new Map();
|
||||
|
||||
constructor(private readonly customBracketPairs: readonly [string, string][]) {
|
||||
constructor(private readonly denseKeyProvider: DenseKeyProvider<string>) {
|
||||
}
|
||||
|
||||
public didLanguageChange(languageId: LanguageId): boolean {
|
||||
|
@ -93,14 +112,14 @@ export class LanguageAgnosticBracketTokens {
|
|||
if (!existing) {
|
||||
return false;
|
||||
}
|
||||
const newRegExpStr = BracketTokens.createFromLanguage(languageId, this.customBracketPairs).getRegExpStr();
|
||||
const newRegExpStr = BracketTokens.createFromLanguage(languageId, this.denseKeyProvider).getRegExpStr();
|
||||
return existing.getRegExpStr() !== newRegExpStr;
|
||||
}
|
||||
|
||||
getSingleLanguageBracketTokens(languageId: LanguageId): BracketTokens {
|
||||
let singleLanguageBracketTokens = this.languageIdToBracketTokens.get(languageId);
|
||||
if (!singleLanguageBracketTokens) {
|
||||
singleLanguageBracketTokens = BracketTokens.createFromLanguage(languageId, this.customBracketPairs);
|
||||
singleLanguageBracketTokens = BracketTokens.createFromLanguage(languageId, this.denseKeyProvider);
|
||||
this.languageIdToBracketTokens.set(languageId, singleLanguageBracketTokens);
|
||||
}
|
||||
return singleLanguageBracketTokens;
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
|
||||
import { AstNode, AstNodeKind, BracketAstNode, InvalidBracketAstNode, ListAstNode, PairAstNode, TextAstNode } from './ast';
|
||||
import { BeforeEditPositionMapper, TextEditInfo } from './beforeEditPositionMapper';
|
||||
import { DenseKeyProvider, SmallImmutableSet } from './smallImmutableSet';
|
||||
import { SmallImmutableSet } from './smallImmutableSet';
|
||||
import { lengthGetLineCount, lengthIsZero, lengthLessThanEqual } from './length';
|
||||
import { concat23Trees } from './concat23Trees';
|
||||
import { NodeReader } from './nodeReader';
|
||||
import { Tokenizer, TokenKind } from './tokenizer';
|
||||
import { OpeningBracketId, Tokenizer, TokenKind } from './tokenizer';
|
||||
|
||||
export function parseDocument(tokenizer: Tokenizer, edits: TextEditInfo[], oldNode: AstNode | undefined, denseKeyProvider: DenseKeyProvider<number>): AstNode {
|
||||
const parser = new Parser(tokenizer, edits, oldNode, denseKeyProvider);
|
||||
export function parseDocument(tokenizer: Tokenizer, edits: TextEditInfo[], oldNode: AstNode | undefined): AstNode {
|
||||
const parser = new Parser(tokenizer, edits, oldNode);
|
||||
return parser.parseDocument();
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ class Parser {
|
|||
private readonly tokenizer: Tokenizer,
|
||||
edits: TextEditInfo[],
|
||||
oldNode: AstNode | undefined,
|
||||
private readonly denseKeyProvider: DenseKeyProvider<number>,
|
||||
) {
|
||||
this.oldNodeReader = oldNode ? new NodeReader(oldNode) : undefined;
|
||||
this.positionMapper = new BeforeEditPositionMapper(edits, tokenizer.length);
|
||||
|
@ -59,7 +58,7 @@ class Parser {
|
|||
}
|
||||
|
||||
private parseList(
|
||||
expectedClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<OpeningBracketId>,
|
||||
): AstNode | null {
|
||||
const items = new Array<AstNode>();
|
||||
|
||||
|
@ -68,12 +67,12 @@ class Parser {
|
|||
if (
|
||||
!token ||
|
||||
(token.kind === TokenKind.ClosingBracket &&
|
||||
expectedClosingCategories.has(token.category, this.denseKeyProvider))
|
||||
token.bracketIds.intersects(openedBracketIds))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
const child = this.parseChild(expectedClosingCategories);
|
||||
const child = this.parseChild(openedBracketIds);
|
||||
if (child.kind === AstNodeKind.List && child.children.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
@ -86,7 +85,7 @@ class Parser {
|
|||
}
|
||||
|
||||
private parseChild(
|
||||
expectingClosingCategories: SmallImmutableSet<number>,
|
||||
openedBracketIds: SmallImmutableSet<number>,
|
||||
): AstNode {
|
||||
if (this.oldNodeReader) {
|
||||
const maxCacheableLength = this.positionMapper.getDistanceToNextChange(this.tokenizer.offset);
|
||||
|
@ -97,7 +96,7 @@ class Parser {
|
|||
}
|
||||
|
||||
const endLineDidChange = lengthGetLineCount(curNode.length) === lengthGetLineCount(maxCacheableLength);
|
||||
const canBeReused = curNode.canBeReused(expectingClosingCategories, endLineDidChange);
|
||||
const canBeReused = curNode.canBeReused(openedBracketIds, endLineDidChange);
|
||||
return canBeReused;
|
||||
});
|
||||
|
||||
|
@ -115,31 +114,29 @@ class Parser {
|
|||
|
||||
switch (token.kind) {
|
||||
case TokenKind.ClosingBracket:
|
||||
return new InvalidBracketAstNode(token.category, token.length, this.denseKeyProvider);
|
||||
return new InvalidBracketAstNode(token.bracketIds, token.length);
|
||||
|
||||
case TokenKind.Text:
|
||||
return token.astNode as TextAstNode;
|
||||
|
||||
case TokenKind.OpeningBracket:
|
||||
const set = expectingClosingCategories.add(token.category, this.denseKeyProvider);
|
||||
const set = openedBracketIds.merge(token.bracketIds);
|
||||
const child = this.parseList(set);
|
||||
|
||||
const nextToken = this.tokenizer.peek();
|
||||
if (
|
||||
nextToken &&
|
||||
nextToken.kind === TokenKind.ClosingBracket &&
|
||||
nextToken.category === token.category
|
||||
(nextToken.bracketId === token.bracketId || nextToken.bracketIds.intersects(token.bracketIds))
|
||||
) {
|
||||
this.tokenizer.read();
|
||||
return PairAstNode.create(
|
||||
token.category,
|
||||
token.astNode as BracketAstNode,
|
||||
child,
|
||||
nextToken.astNode as BracketAstNode
|
||||
);
|
||||
} else {
|
||||
return PairAstNode.create(
|
||||
token.category,
|
||||
token.astNode as BracketAstNode,
|
||||
child,
|
||||
null
|
||||
|
|
|
@ -37,7 +37,7 @@ export class SmallImmutableSet<T> {
|
|||
) {
|
||||
}
|
||||
|
||||
public add(value: T, keyProvider: DenseKeyProvider<T>): SmallImmutableSet<T> {
|
||||
public add(value: T, keyProvider: IDenseKeyProvider<T>): SmallImmutableSet<T> {
|
||||
const key = keyProvider.getKey(value);
|
||||
let idx = key >> 5; // divided by 32
|
||||
if (idx === 0) {
|
||||
|
@ -59,7 +59,7 @@ export class SmallImmutableSet<T> {
|
|||
return SmallImmutableSet.create(this.items, newItems);
|
||||
}
|
||||
|
||||
public has(value: T, keyProvider: DenseKeyProvider<T>): boolean {
|
||||
public has(value: T, keyProvider: IDenseKeyProvider<T>): boolean {
|
||||
const key = keyProvider.getKey(value);
|
||||
let idx = key >> 5; // divided by 32
|
||||
if (idx === 0) {
|
||||
|
@ -129,6 +129,16 @@ export class SmallImmutableSet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface IDenseKeyProvider<T> {
|
||||
getKey(value: T): number;
|
||||
}
|
||||
|
||||
export const identityKeyProvider: IDenseKeyProvider<number> = {
|
||||
getKey(value: number) {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Assigns values a unique incrementing key.
|
||||
*/
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
import { NotSupportedError } from 'vs/base/common/errors';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { LanguageId, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||
import { SmallImmutableSet } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
|
||||
import { StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||
import { BracketAstNode, TextAstNode } from './ast';
|
||||
import { BracketTokens, LanguageAgnosticBracketTokens } from './brackets';
|
||||
import { lengthGetColumnCountIfZeroLineCount, Length, lengthAdd, lengthDiff, lengthToObj, lengthZero, toLength } from './length';
|
||||
|
@ -28,12 +29,24 @@ export const enum TokenKind {
|
|||
ClosingBracket = 2,
|
||||
}
|
||||
|
||||
export type OpeningBracketId = number;
|
||||
|
||||
export class Token {
|
||||
constructor(
|
||||
readonly length: Length,
|
||||
readonly kind: TokenKind,
|
||||
readonly category: number,
|
||||
readonly languageId: LanguageId,
|
||||
/**
|
||||
* If this token is an opening bracket, this is the id of the opening bracket.
|
||||
* If this token is a closing bracket, this is the id of the first opening bracket that is closed by this bracket.
|
||||
* Otherwise, it is -1.
|
||||
*/
|
||||
readonly bracketId: OpeningBracketId,
|
||||
/**
|
||||
* If this token is an opening bracket, this just contains `bracketId`.
|
||||
* If this token is a closing bracket, this lists all opening bracket ids, that it closes.
|
||||
* Otherwise, it is empty.
|
||||
*/
|
||||
readonly bracketIds: SmallImmutableSet<OpeningBracketId>,
|
||||
readonly astNode: BracketAstNode | TextAstNode | undefined,
|
||||
) { }
|
||||
}
|
||||
|
@ -229,7 +242,7 @@ class NonPeekableTextBufferTokenizer {
|
|||
}
|
||||
|
||||
const length = lengthDiff(startLineIdx, startLineCharOffset, this.lineIdx, this.lineCharOffset);
|
||||
return new Token(length, TokenKind.Text, -1, -1, new TextAstNode(length));
|
||||
return new Token(length, TokenKind.Text, -1, SmallImmutableSet.getEmpty(), new TextAstNode(length));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,7 +268,7 @@ export class FastTokenizer implements Tokenizer {
|
|||
for (let i = 0; i < 60; i++) {
|
||||
smallTextTokens0Line.push(
|
||||
new Token(
|
||||
toLength(0, i), TokenKind.Text, -1, -1,
|
||||
toLength(0, i), TokenKind.Text, -1, SmallImmutableSet.getEmpty(),
|
||||
new TextAstNode(toLength(0, i))
|
||||
)
|
||||
);
|
||||
|
@ -265,7 +278,7 @@ export class FastTokenizer implements Tokenizer {
|
|||
for (let i = 0; i < 60; i++) {
|
||||
smallTextTokens1Line.push(
|
||||
new Token(
|
||||
toLength(1, i), TokenKind.Text, -1, -1,
|
||||
toLength(1, i), TokenKind.Text, -1, SmallImmutableSet.getEmpty(),
|
||||
new TextAstNode(toLength(1, i))
|
||||
)
|
||||
);
|
||||
|
@ -288,7 +301,7 @@ export class FastTokenizer implements Tokenizer {
|
|||
token = smallTextTokens0Line[colCount];
|
||||
} else {
|
||||
const length = toLength(0, colCount);
|
||||
token = new Token(length, TokenKind.Text, -1, -1, new TextAstNode(length));
|
||||
token = new Token(length, TokenKind.Text, -1, SmallImmutableSet.getEmpty(), new TextAstNode(length));
|
||||
}
|
||||
} else {
|
||||
const lineCount = curLineCount - lastTokenEndLine;
|
||||
|
@ -297,7 +310,7 @@ export class FastTokenizer implements Tokenizer {
|
|||
token = smallTextTokens1Line[colCount];
|
||||
} else {
|
||||
const length = toLength(lineCount, colCount);
|
||||
token = new Token(length, TokenKind.Text, -1, -1, new TextAstNode(length));
|
||||
token = new Token(length, TokenKind.Text, -1, SmallImmutableSet.getEmpty(), new TextAstNode(length));
|
||||
}
|
||||
}
|
||||
tokens.push(token);
|
||||
|
@ -318,7 +331,7 @@ export class FastTokenizer implements Tokenizer {
|
|||
const length = (lastTokenEndLine === curLineCount)
|
||||
? toLength(0, offset - lastTokenEndOffset)
|
||||
: toLength(curLineCount - lastTokenEndLine, offset - lastLineBreakOffset);
|
||||
tokens.push(new Token(length, TokenKind.Text, -1, -1, new TextAstNode(length)));
|
||||
tokens.push(new Token(length, TokenKind.Text, -1, SmallImmutableSet.getEmpty(), new TextAstNode(length)));
|
||||
}
|
||||
|
||||
this.length = toLength(curLineCount, offset - lastLineBreakOffset);
|
||||
|
|
|
@ -28,7 +28,13 @@ export class CharacterPairSupport {
|
|||
if (config.colorizedBracketPairs) {
|
||||
this._colorizedBracketPairs = config.colorizedBracketPairs.map(b => [b[0], b[1]]);
|
||||
} else if (config.brackets) {
|
||||
this._colorizedBracketPairs = config.brackets.map(b => [b[0], b[1]]);
|
||||
this._colorizedBracketPairs = config.brackets
|
||||
.map((b) => [b[0], b[1]] as [string, string])
|
||||
// Many languages set < ... > as bracket pair, even though they also use it as comparison operator.
|
||||
// This leads to problems when colorizing this bracket, so we exclude it by default.
|
||||
// Languages can still override this by configuring `colorizedBracketPairs`
|
||||
// https://github.com/microsoft/vscode/issues/132476
|
||||
.filter((p) => !(p[0] === '<' && p[1] === '>'));
|
||||
} else {
|
||||
this._colorizedBracketPairs = [];
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contri
|
|||
import * as nls from 'vs/nls';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
@ -966,7 +967,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
|
|||
},
|
||||
flexibleHeight,
|
||||
flexibleWidth,
|
||||
flexibleMaxHeight: 118
|
||||
flexibleMaxHeight: 118,
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this._keybindingService)
|
||||
}, this._contextKeyService, true));
|
||||
this._findInput.setRegex(!!this._state.isRegex);
|
||||
this._findInput.setCaseSensitive(!!this._state.matchCase);
|
||||
|
@ -1105,7 +1107,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
|
|||
history: [],
|
||||
flexibleHeight,
|
||||
flexibleWidth,
|
||||
flexibleMaxHeight: 118
|
||||
flexibleMaxHeight: 118,
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this._keybindingService)
|
||||
}, this._contextKeyService, true));
|
||||
this._replaceInput.setPreserveCase(!!this._state.preserveCase);
|
||||
this._register(this._replaceInput.onKeyDown((e) => this._onReplaceInputKeyDown(e)));
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import assert = require('assert');
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairColorizer/brackets';
|
||||
import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
|
||||
import { Token, TokenKind } from 'vs/editor/common/model/bracketPairColorizer/tokenizer';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
|
||||
suite('Bracket Pair Colorizer - Brackets', () => {
|
||||
test('Basic', () => {
|
||||
const languageId = 3;
|
||||
const mode1 = new LanguageIdentifier('testMode1', languageId);
|
||||
const denseKeyProvider = new DenseKeyProvider<string>();
|
||||
const getImmutableSet = (elements: string[]) => {
|
||||
let newSet = SmallImmutableSet.getEmpty();
|
||||
elements.forEach(x => newSet = newSet.add(`${languageId}:::${x}`, denseKeyProvider));
|
||||
return newSet;
|
||||
};
|
||||
const getKey = (value: string) => {
|
||||
return denseKeyProvider.getKey(`${languageId}:::${value}`);
|
||||
};
|
||||
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(LanguageConfigurationRegistry.register(mode1, {
|
||||
brackets: [
|
||||
['{', '}'], ['[', ']'], ['(', ')'],
|
||||
['begin', 'end'], ['case', 'endcase'], ['casez', 'endcase'], // Verilog
|
||||
['\\left(', '\\right)'], ['\\left(', '\\right.'], ['\\left.', '\\right)'], // LaTeX Parentheses
|
||||
['\\left[', '\\right]'], ['\\left[', '\\right.'], ['\\left.', '\\right]'] // LaTeX Brackets
|
||||
]
|
||||
}));
|
||||
|
||||
const brackets = new LanguageAgnosticBracketTokens(denseKeyProvider);
|
||||
const bracketsExpected = [
|
||||
{ text: '{', length: 1, kind: 'OpeningBracket', bracketId: getKey('{'), bracketIds: getImmutableSet(['{']) },
|
||||
{ text: '[', length: 1, kind: 'OpeningBracket', bracketId: getKey('['), bracketIds: getImmutableSet(['[']) },
|
||||
{ text: '(', length: 1, kind: 'OpeningBracket', bracketId: getKey('('), bracketIds: getImmutableSet(['(']) },
|
||||
{ text: 'begin', length: 5, kind: 'OpeningBracket', bracketId: getKey('begin'), bracketIds: getImmutableSet(['begin']) },
|
||||
{ text: 'case', length: 4, kind: 'OpeningBracket', bracketId: getKey('case'), bracketIds: getImmutableSet(['case']) },
|
||||
{ text: 'casez', length: 5, kind: 'OpeningBracket', bracketId: getKey('casez'), bracketIds: getImmutableSet(['casez']) },
|
||||
{ text: '\\left(', length: 6, kind: 'OpeningBracket', bracketId: getKey('\\left('), bracketIds: getImmutableSet(['\\left(']) },
|
||||
{ text: '\\left.', length: 6, kind: 'OpeningBracket', bracketId: getKey('\\left.'), bracketIds: getImmutableSet(['\\left.']) },
|
||||
{ text: '\\left[', length: 6, kind: 'OpeningBracket', bracketId: getKey('\\left['), bracketIds: getImmutableSet(['\\left[']) },
|
||||
|
||||
{ text: '}', length: 1, kind: 'ClosingBracket', bracketId: getKey('{'), bracketIds: getImmutableSet(['{']) },
|
||||
{ text: ']', length: 1, kind: 'ClosingBracket', bracketId: getKey('['), bracketIds: getImmutableSet(['[']) },
|
||||
{ text: ')', length: 1, kind: 'ClosingBracket', bracketId: getKey('('), bracketIds: getImmutableSet(['(']) },
|
||||
{ text: 'end', length: 3, kind: 'ClosingBracket', bracketId: getKey('begin'), bracketIds: getImmutableSet(['begin']) },
|
||||
{ text: 'endcase', length: 7, kind: 'ClosingBracket', bracketId: getKey('case'), bracketIds: getImmutableSet(['case', 'casez']) },
|
||||
{ text: '\\right)', length: 7, kind: 'ClosingBracket', bracketId: getKey('\\left('), bracketIds: getImmutableSet(['\\left(', '\\left.']) },
|
||||
{ text: '\\right.', length: 7, kind: 'ClosingBracket', bracketId: getKey('\\left('), bracketIds: getImmutableSet(['\\left(', '\\left[']) },
|
||||
{ text: '\\right]', length: 7, kind: 'ClosingBracket', bracketId: getKey('\\left['), bracketIds: getImmutableSet(['\\left[', '\\left.']) }
|
||||
];
|
||||
const bracketsActual = bracketsExpected.map(x => tokenToObject(brackets.getToken(x.text, 3), x.text));
|
||||
|
||||
assert.deepStrictEqual(bracketsActual, bracketsExpected);
|
||||
|
||||
disposableStore.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
function tokenToObject(token: Token | undefined, text: string): any {
|
||||
if (token === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
text: text,
|
||||
length: token.length,
|
||||
bracketId: token.bracketId,
|
||||
bracketIds: token.bracketIds,
|
||||
kind: {
|
||||
[TokenKind.ClosingBracket]: 'ClosingBracket',
|
||||
[TokenKind.OpeningBracket]: 'OpeningBracket',
|
||||
[TokenKind.Text]: 'Text',
|
||||
}[token.kind],
|
||||
};
|
||||
}
|
|
@ -33,12 +33,12 @@ suite('Bracket Pair Colorizer - mergeItems', () => {
|
|||
}
|
||||
}
|
||||
|
||||
if (!node1.unopenedBrackets.equals(node2.unopenedBrackets)) {
|
||||
if (!node1.missingBracketIds.equals(node2.missingBracketIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node1.kind === AstNodeKind.Pair && node2.kind === AstNodeKind.Pair) {
|
||||
return node1.category === node2.category;
|
||||
return true;
|
||||
} else if (node1.kind === node2.kind) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
|
|||
import { TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairColorizer/brackets';
|
||||
import { Length, lengthAdd, lengthsToRange, lengthZero } from 'vs/editor/common/model/bracketPairColorizer/length';
|
||||
import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairColorizer/smallImmutableSet';
|
||||
import { TextBufferTokenizer, Token, Tokenizer, TokenKind } from 'vs/editor/common/model/bracketPairColorizer/tokenizer';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { IState, ITokenizationSupport, LanguageId, LanguageIdentifier, MetadataConsts, StandardTokenType, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
|
@ -16,7 +17,17 @@ import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
|||
|
||||
suite('Bracket Pair Colorizer - Tokenizer', () => {
|
||||
test('Basic', () => {
|
||||
const mode1 = new LanguageIdentifier('testMode1', 2);
|
||||
const languageId = 2;
|
||||
const mode1 = new LanguageIdentifier('testMode1', languageId);
|
||||
const denseKeyProvider = new DenseKeyProvider<string>();
|
||||
const getImmutableSet = (elements: string[]) => {
|
||||
let newSet = SmallImmutableSet.getEmpty();
|
||||
elements.forEach(x => newSet = newSet.add(`${languageId}:::${x}`, denseKeyProvider));
|
||||
return newSet;
|
||||
};
|
||||
const getKey = (value: string) => {
|
||||
return denseKeyProvider.getKey(`${languageId}:::${value}`);
|
||||
};
|
||||
|
||||
const tStandard = (text: string) => new TokenInfo(text, mode1.id, StandardTokenType.Other);
|
||||
const tComment = (text: string) => new TokenInfo(text, mode1.id, StandardTokenType.Comment);
|
||||
|
@ -28,10 +39,10 @@ suite('Bracket Pair Colorizer - Tokenizer', () => {
|
|||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(TokenizationRegistry.register(mode1.language, document.getTokenizationSupport()));
|
||||
disposableStore.add(LanguageConfigurationRegistry.register(mode1, {
|
||||
brackets: [['{', '}'], ['[', ']'], ['(', ')']],
|
||||
brackets: [['{', '}'], ['[', ']'], ['(', ')'], ['begin', 'end']],
|
||||
}));
|
||||
|
||||
const brackets = new LanguageAgnosticBracketTokens([['begin', 'end']]);
|
||||
const brackets = new LanguageAgnosticBracketTokens(denseKeyProvider);
|
||||
|
||||
const model = createTextModel(document.getText(), {}, mode1);
|
||||
model.forceTokenization(model.getLineCount());
|
||||
|
@ -39,16 +50,16 @@ suite('Bracket Pair Colorizer - Tokenizer', () => {
|
|||
const tokens = readAllTokens(new TextBufferTokenizer(model, brackets));
|
||||
|
||||
assert.deepStrictEqual(toArr(tokens, model), [
|
||||
{ category: -1, kind: 'Text', languageId: -1, text: ' ', },
|
||||
{ category: 2000, kind: 'OpeningBracket', languageId: 2, text: '{', },
|
||||
{ category: -1, kind: 'Text', languageId: -1, text: ' ', },
|
||||
{ category: 2000, kind: 'ClosingBracket', languageId: 2, text: '}', },
|
||||
{ category: -1, kind: 'Text', languageId: -1, text: ' ', },
|
||||
{ category: 2004, kind: 'OpeningBracket', languageId: 2, text: 'begin', },
|
||||
{ category: -1, kind: 'Text', languageId: -1, text: ' ', },
|
||||
{ category: 2004, kind: 'ClosingBracket', languageId: 2, text: 'end', },
|
||||
{ category: -1, kind: 'Text', languageId: -1, text: '\nhello{', },
|
||||
{ category: 2000, kind: 'ClosingBracket', languageId: 2, text: '}', }
|
||||
{ bracketId: -1, bracketIds: getImmutableSet([]), kind: 'Text', text: ' ', },
|
||||
{ bracketId: getKey('{'), bracketIds: getImmutableSet(['{']), kind: 'OpeningBracket', text: '{', },
|
||||
{ bracketId: -1, bracketIds: getImmutableSet([]), kind: 'Text', text: ' ', },
|
||||
{ bracketId: getKey('{'), bracketIds: getImmutableSet(['{']), kind: 'ClosingBracket', text: '}', },
|
||||
{ bracketId: -1, bracketIds: getImmutableSet([]), kind: 'Text', text: ' ', },
|
||||
{ bracketId: getKey('begin'), bracketIds: getImmutableSet(['begin']), kind: 'OpeningBracket', text: 'begin', },
|
||||
{ bracketId: -1, bracketIds: getImmutableSet([]), kind: 'Text', text: ' ', },
|
||||
{ bracketId: getKey('begin'), bracketIds: getImmutableSet(['begin']), kind: 'ClosingBracket', text: 'end', },
|
||||
{ bracketId: -1, bracketIds: getImmutableSet([]), kind: 'Text', text: '\nhello{', },
|
||||
{ bracketId: getKey('{'), bracketIds: getImmutableSet(['{']), kind: 'ClosingBracket', text: '}', }
|
||||
]);
|
||||
|
||||
disposableStore.dispose();
|
||||
|
@ -80,13 +91,13 @@ function toArr(tokens: Token[], model: TextModel): any[] {
|
|||
function tokenToObj(token: Token, offset: Length, model: TextModel): any {
|
||||
return {
|
||||
text: model.getValueInRange(lengthsToRange(offset, lengthAdd(offset, token.length))),
|
||||
category: token.category,
|
||||
bracketId: token.bracketId,
|
||||
bracketIds: token.bracketIds,
|
||||
kind: {
|
||||
[TokenKind.ClosingBracket]: 'ClosingBracket',
|
||||
[TokenKind.OpeningBracket]: 'OpeningBracket',
|
||||
[TokenKind.Text]: 'Text',
|
||||
}[token.kind],
|
||||
languageId: token.languageId,
|
||||
}[token.kind]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
10
src/vs/platform/browser/historyWidgetKeybindingHint.ts
Normal file
10
src/vs/platform/browser/historyWidgetKeybindingHint.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
|
||||
export function showHistoryKeybindingHint(keybindingService: IKeybindingService): boolean {
|
||||
return keybindingService.lookupKeybinding('history.showPrevious')?.getElectronAccelerator() === 'Up' && keybindingService.lookupKeybinding('history.showNext')?.getElectronAccelerator() === 'Down';
|
||||
}
|
|
@ -290,7 +290,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
|||
// to flush the contents to disk if possible.
|
||||
if (this.writeHandles.delete(fd) && this.canFlush) {
|
||||
try {
|
||||
await Promises.fdatasync(fd);
|
||||
await Promises.fdatasync(fd); // https://github.com/microsoft/vscode/issues/9589
|
||||
} catch (error) {
|
||||
// In some exotic setups it is well possible that node fails to sync
|
||||
// In that case we disable flushing and log the error to our logger
|
||||
|
|
|
@ -84,7 +84,6 @@ export const enum TerminalSettingId {
|
|||
SplitCwd = 'terminal.integrated.splitCwd',
|
||||
WindowsEnableConpty = 'terminal.integrated.windowsEnableConpty',
|
||||
WordSeparators = 'terminal.integrated.wordSeparators',
|
||||
TitleMode = 'terminal.integrated.titleMode',
|
||||
EnableFileLinks = 'terminal.integrated.enableFileLinks',
|
||||
UnicodeVersion = 'terminal.integrated.unicodeVersion',
|
||||
ExperimentalLinkProvider = 'terminal.integrated.experimentalLinkProvider',
|
||||
|
@ -95,7 +94,6 @@ export const enum TerminalSettingId {
|
|||
PersistentSessionReviveProcess = 'terminal.integrated.persistentSessionReviveProcess',
|
||||
CustomGlyphs = 'terminal.integrated.customGlyphs',
|
||||
PersistentSessionScrollback = 'terminal.integrated.persistentSessionScrollback',
|
||||
PersistentSessionExperimentalSerializer = 'terminal.integrated.persistentSessionExperimentalSerializer',
|
||||
InheritEnv = 'terminal.integrated.inheritEnv',
|
||||
ShowLinkHover = 'terminal.integrated.showLinkHover',
|
||||
}
|
||||
|
@ -560,7 +558,6 @@ export interface IReconnectConstants {
|
|||
graceTime: number;
|
||||
shortGraceTime: number;
|
||||
scrollback: number;
|
||||
useExperimentalSerialization: boolean;
|
||||
}
|
||||
|
||||
export const enum LocalReconnectConstants {
|
||||
|
|
|
@ -366,12 +366,6 @@ const terminalPlatformConfiguration: IConfigurationNode = {
|
|||
type: 'number',
|
||||
default: 100
|
||||
},
|
||||
[TerminalSettingId.PersistentSessionExperimentalSerializer]: {
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
description: localize('terminal.integrated.persistentSessionExperimentalSerializer', "Whether to use a more efficient experimental approach for restoring the terminal's buffer. This setting requires a restart to take effect."),
|
||||
type: 'boolean',
|
||||
default: true
|
||||
},
|
||||
[TerminalSettingId.ShowLinkHover]: {
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
description: localize('terminal.integrated.showLinkHover', "Whether to show hovers for links in the terminal output."),
|
||||
|
|
|
@ -17,14 +17,7 @@ export interface IRemoteTerminalProcessReplayEvent {
|
|||
events: ReplayEntry[];
|
||||
}
|
||||
|
||||
export interface ITerminalSerializer {
|
||||
handleData(data: string): void;
|
||||
handleResize(cols: number, rows: number): void;
|
||||
generateReplayEvent(normalBufferOnly?: boolean): Promise<IPtyHostProcessReplayEvent>;
|
||||
setUnicodeVersion?(version: '6' | '11'): void;
|
||||
}
|
||||
|
||||
export class TerminalRecorder implements ITerminalSerializer {
|
||||
export class TerminalRecorder {
|
||||
|
||||
private _entries: RecorderEntry[];
|
||||
private _totalDataLength: number = 0;
|
||||
|
|
|
@ -26,13 +26,11 @@ server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(h
|
|||
const reconnectConstants: IReconnectConstants = {
|
||||
graceTime: parseInt(process.env.VSCODE_RECONNECT_GRACE_TIME || '0'),
|
||||
shortGraceTime: parseInt(process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME || '0'),
|
||||
scrollback: parseInt(process.env.VSCODE_RECONNECT_SCROLLBACK || '100'),
|
||||
useExperimentalSerialization: !!parseInt(process.env.VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION || '1')
|
||||
scrollback: parseInt(process.env.VSCODE_RECONNECT_SCROLLBACK || '100')
|
||||
};
|
||||
delete process.env.VSCODE_RECONNECT_GRACE_TIME;
|
||||
delete process.env.VSCODE_RECONNECT_SHORT_GRACE_TIME;
|
||||
delete process.env.VSCODE_RECONNECT_SCROLLBACK;
|
||||
delete process.env.VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION;
|
||||
|
||||
const ptyService = new PtyService(lastPtyId, logService, reconnectConstants);
|
||||
server.registerChannel(TerminalIpcChannels.PtyHost, ProxyChannel.fromService(ptyService));
|
||||
|
|
|
@ -121,8 +121,7 @@ export class PtyHostService extends Disposable implements IPtyService {
|
|||
VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client,
|
||||
VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime,
|
||||
VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime,
|
||||
VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback,
|
||||
VSCODE_RECONNECT_EXPERIMENTAL_SERIALIZATION: this._reconnectConstants.useExperimentalSerialization ? 1 : 0
|
||||
VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -19,7 +19,6 @@ import { Terminal as XtermTerminal } from 'xterm-headless';
|
|||
import type { ISerializeOptions, SerializeAddon as XtermSerializeAddon } from 'xterm-addon-serialize';
|
||||
import type { Unicode11Addon as XtermUnicode11Addon } from 'xterm-addon-unicode11';
|
||||
import { IGetTerminalLayoutInfoArgs, IProcessDetails, IPtyHostProcessReplayEvent, ISetTerminalLayoutInfoArgs, ITerminalTabLayoutInfoDto } from 'vs/platform/terminal/common/terminalProcess';
|
||||
import { ITerminalSerializer, TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder';
|
||||
import { getWindowsBuildNumber } from 'vs/platform/terminal/node/terminalEnvironment';
|
||||
import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess';
|
||||
import { localize } from 'vs/nls';
|
||||
|
@ -471,18 +470,13 @@ export class PersistentTerminalProcess extends Disposable {
|
|||
super();
|
||||
this._logService.trace('persistentTerminalProcess#ctor', _persistentProcessId, arguments);
|
||||
this._wasRevived = reviveBuffer !== undefined;
|
||||
|
||||
if (reconnectConstants.useExperimentalSerialization) {
|
||||
this._serializer = new XtermSerializer(
|
||||
cols,
|
||||
rows,
|
||||
reconnectConstants.scrollback,
|
||||
unicodeVersion,
|
||||
reviveBuffer
|
||||
);
|
||||
} else {
|
||||
this._serializer = new TerminalRecorder(cols, rows);
|
||||
}
|
||||
this._serializer = new XtermSerializer(
|
||||
cols,
|
||||
rows,
|
||||
reconnectConstants.scrollback,
|
||||
unicodeVersion,
|
||||
reviveBuffer
|
||||
);
|
||||
this._orphanQuestionBarrier = null;
|
||||
this._orphanQuestionReplyTime = 0;
|
||||
this._disconnectRunner1 = this._register(new ProcessTimeRunOnceScheduler(() => {
|
||||
|
@ -782,3 +776,10 @@ export interface ISerializedTerminalState {
|
|||
replayEvent: IPtyHostProcessReplayEvent;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export interface ITerminalSerializer {
|
||||
handleData(data: string): void;
|
||||
handleResize(cols: number, rows: number): void;
|
||||
generateReplayEvent(normalBufferOnly?: boolean): Promise<IPtyHostProcessReplayEvent>;
|
||||
setUnicodeVersion?(version: '6' | '11'): void;
|
||||
}
|
||||
|
|
|
@ -151,7 +151,6 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
|||
this._initialCwd = cwd;
|
||||
this._properties[ProcessPropertyType.InitialCwd] = this._initialCwd;
|
||||
this._properties[ProcessPropertyType.Cwd] = this._initialCwd;
|
||||
|
||||
const useConpty = windowsEnableConpty && process.platform === 'win32' && getWindowsBuildNumber() >= 18309;
|
||||
this._ptyOptions = {
|
||||
name,
|
||||
|
@ -164,6 +163,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
|||
// This option will force conpty to not redraw the whole viewport on launch
|
||||
conptyInheritCursor: useConpty && !!shellLaunchConfig.initialText
|
||||
};
|
||||
const osRelease = os.release().split('.');
|
||||
// Delay resizes to avoid conpty not respecting very early resize calls
|
||||
if (isWindows) {
|
||||
if (useConpty && cols === 0 && rows === 0 && this.shellLaunchConfig.executable?.endsWith('Git\\bin\\bash.exe')) {
|
||||
|
@ -182,7 +182,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess
|
|||
this._register(this._windowsShellHelper.onShellTypeChanged(e => this._onProcessShellTypeChanged.fire(e)));
|
||||
this._register(this._windowsShellHelper.onShellNameChanged(e => this._onProcessTitleChanged.fire(e)));
|
||||
});
|
||||
} else {
|
||||
} else if (isLinux || (osRelease.length > 0 && parseInt(osRelease[0]) < 20)) {
|
||||
this.capabilities.push(ProcessCapability.CwdDetection);
|
||||
}
|
||||
}
|
||||
|
|
31
src/vs/vscode.d.ts
vendored
31
src/vs/vscode.d.ts
vendored
|
@ -5858,6 +5858,11 @@ declare module 'vscode' {
|
|||
*/
|
||||
readonly exitStatus: TerminalExitStatus | undefined;
|
||||
|
||||
/**
|
||||
* The current state of the {@link Terminal}.
|
||||
*/
|
||||
readonly state: TerminalState;
|
||||
|
||||
/**
|
||||
* Send text to the terminal. The text is written to the stdin of the underlying pty process
|
||||
* (shell) of the terminal.
|
||||
|
@ -5887,6 +5892,27 @@ declare module 'vscode' {
|
|||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state of a {@link Terminal}.
|
||||
*/
|
||||
export interface TerminalState {
|
||||
/**
|
||||
* Whether the {@link Terminal} has been interacted with. Interaction means that the
|
||||
* terminal has sent data to the process which depending on the terminal's _mode_. By
|
||||
* default input is sent when a key is pressed or when a command or extension sends text,
|
||||
* but based on the terminal's mode it can also happen on:
|
||||
*
|
||||
* - a pointer click event
|
||||
* - a pointer scroll event
|
||||
* - a pointer move event
|
||||
* - terminal focus in/out
|
||||
*
|
||||
* For more information on events that can send data see "DEC Private Mode Set (DECSET)" on
|
||||
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
*/
|
||||
readonly isInteractedWith: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides information on a line in a terminal in order to provide links for it.
|
||||
*/
|
||||
|
@ -8662,6 +8688,11 @@ declare module 'vscode' {
|
|||
*/
|
||||
export const onDidCloseTerminal: Event<Terminal>;
|
||||
|
||||
/**
|
||||
* An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed.
|
||||
*/
|
||||
export const onDidChangeTerminalState: Event<Terminal>;
|
||||
|
||||
/**
|
||||
* Represents the current window's state.
|
||||
*/
|
||||
|
|
40
src/vs/vscode.proposed.d.ts
vendored
40
src/vs/vscode.proposed.d.ts
vendored
|
@ -906,46 +906,6 @@ declare module 'vscode' {
|
|||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal state event https://github.com/microsoft/vscode/issues/127717
|
||||
|
||||
/**
|
||||
* Represents the state of a {@link Terminal}.
|
||||
*/
|
||||
export interface TerminalState {
|
||||
/**
|
||||
* Whether the {@link Terminal} has been interacted with. Interaction means that the
|
||||
* terminal has sent data to the process which depending on the terminal's _mode_. By
|
||||
* default input is sent when a key is pressed or when a command or extension sends text,
|
||||
* but based on the terminal's mode it can also happen on:
|
||||
*
|
||||
* - a pointer click event
|
||||
* - a pointer scroll event
|
||||
* - a pointer move event
|
||||
* - terminal focus in/out
|
||||
*
|
||||
* For more information on events that can send data see "DEC Private Mode Set (DECSET)" on
|
||||
* https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
*/
|
||||
// todo@API Maybe, isInteractedWith to align with other isXYZ
|
||||
readonly interactedWith: boolean;
|
||||
}
|
||||
|
||||
export interface Terminal {
|
||||
/**
|
||||
* The current state of the {@link Terminal}.
|
||||
*/
|
||||
readonly state: TerminalState;
|
||||
}
|
||||
|
||||
export namespace window {
|
||||
/**
|
||||
* An {@link Event} which fires when a {@link Terminal.state terminal's state} has changed.
|
||||
*/
|
||||
export const onDidChangeTerminalState: Event<Terminal>;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Terminal location https://github.com/microsoft/vscode/issues/45407
|
||||
|
||||
export interface TerminalOptions {
|
||||
|
|
|
@ -21,7 +21,6 @@ export class MainThreadEditorTabs {
|
|||
private readonly _proxy: IExtHostEditorTabsShape;
|
||||
private readonly _tabModel: Map<number, IEditorTabDto[]> = new Map<number, IEditorTabDto[]>();
|
||||
private _currentlyActiveTab: { groupId: number, tab: IEditorTabDto } | undefined = undefined;
|
||||
private _oldTabModel: IEditorTabDto[] = [];
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
|
@ -111,7 +110,7 @@ export class MainThreadEditorTabs {
|
|||
if (movedTab === undefined) {
|
||||
return;
|
||||
}
|
||||
this._tabModel.get(event.groupId)?.splice(event.oldEditorIndex, 0, movedTab[0]);
|
||||
this._tabModel.get(event.groupId)?.splice(event.editorIndex, 0, movedTab[0]);
|
||||
movedTab[0].isActive = (this._editorGroupsService.activeGroup.id === event.groupId) && this._editorGroupsService.activeGroup.isActive({ resource: URI.revive(movedTab[0].resource), options: { override: movedTab[0].editorId } });
|
||||
// Update the currently active tab
|
||||
if (movedTab[0].isActive) {
|
||||
|
@ -123,7 +122,7 @@ export class MainThreadEditorTabs {
|
|||
}
|
||||
|
||||
private _onDidGroupActivate(event: IEditorsChangeEvent): void {
|
||||
if (event.kind !== GroupChangeKind.GROUP_INDEX) {
|
||||
if (event.kind !== GroupChangeKind.GROUP_INDEX && event.kind !== GroupChangeKind.EDITOR_ACTIVE) {
|
||||
return;
|
||||
}
|
||||
this._findAndUpdateActiveTab();
|
||||
|
@ -146,22 +145,26 @@ export class MainThreadEditorTabs {
|
|||
}, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to compare previous tab model to current one
|
||||
* @param current The current tab model to compare to the previous mode
|
||||
* @returns True if they're equivalent, false otherwise
|
||||
*/
|
||||
private _compareTabsModel(current: IEditorTabDto[]): boolean {
|
||||
if (this._oldTabModel.length !== current.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < current.length; i++) {
|
||||
if (this._oldTabModel[i].resource !== current[i].resource && this._oldTabModel[i].editorId !== current[i].editorId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// TODOD @lramos15 Remove this after done finishing the tab model code
|
||||
// private _eventArrayToString(events: IEditorsChangeEvent[]): void {
|
||||
// let eventString = '[';
|
||||
// events.forEach(event => {
|
||||
// switch (event.kind) {
|
||||
// case GroupChangeKind.GROUP_INDEX: eventString += 'GROUP_INDEX, '; break;
|
||||
// case GroupChangeKind.EDITOR_ACTIVE: eventString += 'EDITOR_ACTIVE, '; break;
|
||||
// case GroupChangeKind.EDITOR_PIN: eventString += 'EDITOR_PIN, '; break;
|
||||
// case GroupChangeKind.EDITOR_OPEN: eventString += 'EDITOR_OPEN, '; break;
|
||||
// case GroupChangeKind.EDITOR_CLOSE: eventString += 'EDITOR_CLOSE, '; break;
|
||||
// case GroupChangeKind.EDITOR_MOVE: eventString += 'EDITOR_MOVE, '; break;
|
||||
// case GroupChangeKind.EDITOR_LABEL: eventString += 'EDITOR_LABEL, '; break;
|
||||
// case GroupChangeKind.GROUP_ACTIVE: eventString += 'GROUP_ACTIVE, '; break;
|
||||
// case GroupChangeKind.GROUP_LOCKED: eventString += 'GROUP_LOCKED, '; break;
|
||||
// default: eventString += 'UNKNOWN, '; break;
|
||||
// }
|
||||
// });
|
||||
// eventString += ']';
|
||||
// console.log(eventString);
|
||||
// }
|
||||
|
||||
private _updateTabsModel(events: IEditorsChangeEvent[]): void {
|
||||
events.forEach(event => {
|
||||
|
@ -173,6 +176,7 @@ export class MainThreadEditorTabs {
|
|||
case GroupChangeKind.EDITOR_CLOSE:
|
||||
this._onDidTabClose(event);
|
||||
break;
|
||||
case GroupChangeKind.EDITOR_ACTIVE:
|
||||
case GroupChangeKind.GROUP_ACTIVE:
|
||||
if (this._editorGroupsService.activeGroup.id !== event.groupId) {
|
||||
return;
|
||||
|
@ -193,9 +197,6 @@ export class MainThreadEditorTabs {
|
|||
// Flatten the map into a singular array to send the ext host
|
||||
let allTabs: IEditorTabDto[] = [];
|
||||
this._tabModel.forEach((tabs) => allTabs = allTabs.concat(tabs));
|
||||
if (!this._compareTabsModel(allTabs)) {
|
||||
this._proxy.$acceptEditorTabs(allTabs);
|
||||
this._oldTabModel = allTabs;
|
||||
}
|
||||
this._proxy.$acceptEditorTabs(allTabs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
|
||||
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData));
|
||||
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService, extHostFileSystemInfo));
|
||||
const extHostLanguages = rpcProtocol.set(ExtHostContext.ExtHostLanguages, new ExtHostLanguages(rpcProtocol, extHostDocuments, extHostCommands.converter));
|
||||
const extHostLanguages = rpcProtocol.set(ExtHostContext.ExtHostLanguages, new ExtHostLanguages(rpcProtocol, extHostDocuments, extHostCommands.converter, uriTransformer));
|
||||
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation));
|
||||
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
|
||||
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
|
||||
|
|
|
@ -17,7 +17,7 @@ import * as extHostProtocol from './extHost.protocol';
|
|||
import { regExpLeadsToEndlessLoop, regExpFlags } from 'vs/base/common/strings';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range as EditorRange } from 'vs/editor/common/core/range';
|
||||
import { isFalsyOrEmpty, isNonEmptyArray, coalesce, asArray } from 'vs/base/common/arrays';
|
||||
import { isFalsyOrEmpty, isNonEmptyArray, coalesce } from 'vs/base/common/arrays';
|
||||
import { isArray, isObject } from 'vs/base/common/types';
|
||||
import { ISelection, Selection } from 'vs/editor/common/core/selection';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
@ -1522,7 +1522,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
|
||||
private static _handlePool: number = 0;
|
||||
|
||||
private readonly _uriTransformer: IURITransformer | null;
|
||||
private readonly _uriTransformer: IURITransformer;
|
||||
private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape;
|
||||
private _documents: ExtHostDocuments;
|
||||
private _commands: ExtHostCommands;
|
||||
|
@ -1533,7 +1533,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
|
||||
constructor(
|
||||
mainContext: extHostProtocol.IMainContext,
|
||||
uriTransformer: IURITransformer | null,
|
||||
uriTransformer: IURITransformer,
|
||||
documents: ExtHostDocuments,
|
||||
commands: ExtHostCommands,
|
||||
diagnostics: ExtHostDiagnostics,
|
||||
|
@ -1550,35 +1550,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF
|
|||
}
|
||||
|
||||
private _transformDocumentSelector(selector: vscode.DocumentSelector): Array<extHostProtocol.IDocumentFilterDto> {
|
||||
return coalesce(asArray(selector).map(sel => this._doTransformDocumentSelector(sel)));
|
||||
}
|
||||
|
||||
private _doTransformDocumentSelector(selector: string | vscode.DocumentFilter): extHostProtocol.IDocumentFilterDto | undefined {
|
||||
if (typeof selector === 'string') {
|
||||
return {
|
||||
$serialized: true,
|
||||
language: selector
|
||||
};
|
||||
}
|
||||
|
||||
if (selector) {
|
||||
return {
|
||||
$serialized: true,
|
||||
language: selector.language,
|
||||
scheme: this._transformScheme(selector.scheme),
|
||||
pattern: typeof selector.pattern === 'undefined' ? undefined : typeConvert.GlobPattern.from(selector.pattern),
|
||||
exclusive: selector.exclusive
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _transformScheme(scheme: string | undefined): string | undefined {
|
||||
if (this._uriTransformer && typeof scheme === 'string') {
|
||||
return this._uriTransformer.transformOutgoingScheme(scheme);
|
||||
}
|
||||
return scheme;
|
||||
return typeConvert.DocumentSelector.from(selector, this._uriTransformer);
|
||||
}
|
||||
|
||||
private _createDisposable(handle: number): Disposable {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { disposableTimeout } from 'vs/base/common/async';
|
|||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
|
||||
export class ExtHostLanguages implements ExtHostLanguagesShape {
|
||||
|
||||
|
@ -23,7 +24,8 @@ export class ExtHostLanguages implements ExtHostLanguagesShape {
|
|||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private readonly _documents: ExtHostDocuments,
|
||||
private readonly _commands: CommandsConverter
|
||||
private readonly _commands: CommandsConverter,
|
||||
private readonly _uriTransformer: IURITransformer | undefined
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguages);
|
||||
}
|
||||
|
@ -108,7 +110,7 @@ export class ExtHostLanguages implements ExtHostLanguagesShape {
|
|||
id: fullyQualifiedId,
|
||||
name: data.name ?? extension.displayName ?? extension.name,
|
||||
source: extension.displayName ?? extension.name,
|
||||
selector: data.selector,
|
||||
selector: typeConvert.DocumentSelector.from(data.selector, this._uriTransformer),
|
||||
label: data.text,
|
||||
detail: data.detail ?? '',
|
||||
severity: data.severity === LanguageStatusSeverity.Error ? Severity.Error : data.severity === LanguageStatusSeverity.Warning ? Severity.Warning : Severity.Info,
|
||||
|
|
|
@ -68,7 +68,7 @@ export class ExtHostTerminal {
|
|||
private _pidPromiseComplete: ((value: number | undefined) => any) | undefined;
|
||||
private _rows: number | undefined;
|
||||
private _exitStatus: vscode.TerminalExitStatus | undefined;
|
||||
private _state: vscode.TerminalState = { interactedWith: false };
|
||||
private _state: vscode.TerminalState = { isInteractedWith: false };
|
||||
|
||||
public isOpen: boolean = false;
|
||||
|
||||
|
@ -218,8 +218,8 @@ export class ExtHostTerminal {
|
|||
}
|
||||
|
||||
public setInteractedWith(): boolean {
|
||||
if (!this._state.interactedWith) {
|
||||
this._state = { interactedWith: true };
|
||||
if (!this._state.isInteractedWith) {
|
||||
this._state = { isInteractedWith: true };
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { asArray, coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
@ -12,6 +12,7 @@ import { parse } from 'vs/base/common/marshalling';
|
|||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { isDefined, isEmptyObject, isNumber, isString } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IURITransformer } from 'vs/base/common/uriIpc';
|
||||
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
|
@ -122,6 +123,41 @@ export namespace Position {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace DocumentSelector {
|
||||
|
||||
export function from(value: vscode.DocumentSelector, uriTransformer?: IURITransformer): extHostProtocol.IDocumentFilterDto[] {
|
||||
return coalesce(asArray(value).map(sel => _doTransformDocumentSelector(sel, uriTransformer)));
|
||||
}
|
||||
|
||||
function _doTransformDocumentSelector(selector: string | vscode.DocumentFilter, uriTransformer: IURITransformer | undefined): extHostProtocol.IDocumentFilterDto | undefined {
|
||||
if (typeof selector === 'string') {
|
||||
return {
|
||||
$serialized: true,
|
||||
language: selector
|
||||
};
|
||||
}
|
||||
|
||||
if (selector) {
|
||||
return {
|
||||
$serialized: true,
|
||||
language: selector.language,
|
||||
scheme: _transformScheme(selector.scheme, uriTransformer),
|
||||
pattern: typeof selector.pattern === 'undefined' ? undefined : GlobPattern.from(selector.pattern),
|
||||
exclusive: selector.exclusive
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function _transformScheme(scheme: string | undefined, uriTransformer: IURITransformer | undefined): string | undefined {
|
||||
if (uriTransformer && typeof scheme === 'string') {
|
||||
return uriTransformer.transformOutgoingScheme(scheme);
|
||||
}
|
||||
return scheme;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace DiagnosticTag {
|
||||
export function from(value: vscode.DiagnosticTag): MarkerTag | undefined {
|
||||
switch (value) {
|
||||
|
|
|
@ -64,6 +64,7 @@ registerThemingParticipant((theme, collector) => {
|
|||
const CONTEXT_BANNER_FOCUSED = new RawContextKey<boolean>('bannerFocused', false, localize('bannerFocused', "Whether the banner has keyboard focus"));
|
||||
|
||||
export class BannerPart extends Part implements IBannerService {
|
||||
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
// #region IView
|
||||
|
@ -80,8 +81,7 @@ export class BannerPart extends Part implements IBannerService {
|
|||
return this.visible ? this.height : 0;
|
||||
}
|
||||
|
||||
private _onDidChangeSize = new Emitter<{ width: number; height: number; } | undefined>();
|
||||
|
||||
private _onDidChangeSize = this._register(new Emitter<{ width: number; height: number; } | undefined>());
|
||||
override get onDidChange() { return this._onDidChangeSize.event; }
|
||||
|
||||
//#endregion
|
||||
|
|
|
@ -36,7 +36,7 @@ import {
|
|||
JoinAllGroupsAction, FocusLeftGroup, FocusAboveGroup, FocusRightGroup, FocusBelowGroup, EditorLayoutSingleAction, EditorLayoutTwoColumnsAction, EditorLayoutThreeColumnsAction, EditorLayoutTwoByTwoGridAction,
|
||||
EditorLayoutTwoRowsAction, EditorLayoutThreeRowsAction, EditorLayoutTwoColumnsBottomAction, EditorLayoutTwoRowsRightAction, NewEditorGroupLeftAction, NewEditorGroupRightAction,
|
||||
NewEditorGroupAboveAction, NewEditorGroupBelowAction, SplitEditorOrthogonalAction, CloseEditorInAllGroupsAction, NavigateToLastEditLocationAction, ToggleGroupSizesAction, ShowAllEditorsByMostRecentlyUsedAction,
|
||||
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction
|
||||
QuickAccessPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction, QuickAccessLeastRecentlyUsedEditorAction, QuickAccessLeastRecentlyUsedEditorInGroupAction, ReOpenInTextEditorAction, DuplicateGroupDownAction, DuplicateGroupLeftAction, DuplicateGroupRightAction, DuplicateGroupUpAction, ToggleEditorTypeAction, SplitEditorToAboveGroupAction, SplitEditorToBelowGroupAction, SplitEditorToFirstGroupAction, SplitEditorToLastGroupAction, SplitEditorToLeftGroupAction, SplitEditorToNextGroupAction, SplitEditorToPreviousGroupAction, SplitEditorToRightGroupAction
|
||||
} from 'vs/workbench/browser/parts/editor/editorActions';
|
||||
import {
|
||||
CLOSE_EDITORS_AND_GROUP_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_EDITOR_GROUP_COMMAND_ID,
|
||||
|
@ -218,6 +218,14 @@ registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToLeftGroup
|
|||
registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToRightGroupAction), 'View: Move Editor into Right Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToAboveGroupAction), 'View: Move Editor into Above Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(MoveEditorToBelowGroupAction), 'View: Move Editor into Below Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToPreviousGroupAction), 'View: Split Editor into Previous Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToNextGroupAction), 'View: Split Editor into Next Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToFirstGroupAction), 'View: Split Editor into First Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLastGroupAction), 'View: Split Editor into Last Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToLeftGroupAction), 'View: Split Editor into Left Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToRightGroupAction), 'View: Split Editor into Right Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToAboveGroupAction), 'View: Split Editor into Above Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(SplitEditorToBelowGroupAction), 'View: Split Editor into Below Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusActiveGroupAction), 'View: Focus Active Editor Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusFirstGroupAction, { primary: KeyMod.CtrlCmd | KeyCode.KEY_1 }), 'View: Focus First Editor Group', CATEGORIES.View.value);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.from(FocusLastGroupAction), 'View: Focus Last Editor Group', CATEGORIES.View.value);
|
||||
|
|
|
@ -13,7 +13,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la
|
|||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
|
||||
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
@ -1608,7 +1608,7 @@ export class MoveEditorLeftInGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'left' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'left' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1622,7 +1622,7 @@ export class MoveEditorRightInGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'right' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'right' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1636,7 +1636,7 @@ export class MoveEditorToPreviousGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'previous', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'previous', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1650,7 +1650,7 @@ export class MoveEditorToNextGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'next', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'next', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1664,7 +1664,7 @@ export class MoveEditorToAboveGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'up', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'up', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1678,7 +1678,7 @@ export class MoveEditorToBelowGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'down', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'down', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1692,7 +1692,7 @@ export class MoveEditorToLeftGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'left', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'left', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1706,7 +1706,7 @@ export class MoveEditorToRightGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'right', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'right', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1720,7 +1720,7 @@ export class MoveEditorToFirstGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'first', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'first', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1734,7 +1734,119 @@ export class MoveEditorToLastGroupAction extends ExecuteCommandAction {
|
|||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'last', by: 'group' } as ActiveEditorMoveArguments);
|
||||
super(id, label, MOVE_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'last', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToPreviousGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToPreviousGroup';
|
||||
static readonly LABEL = localize('splitEditorToPreviousGroup', "Split Editor into Previous Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'previous', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToNextGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToNextGroup';
|
||||
static readonly LABEL = localize('splitEditorToNextGroup', "Split Editor into Next Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'next', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToAboveGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToAboveGroup';
|
||||
static readonly LABEL = localize('splitEditorToAboveGroup', "Split Editor into Above Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'up', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToBelowGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToBelowGroup';
|
||||
static readonly LABEL = localize('splitEditorToBelowGroup', "Split Editor into Below Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'down', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToLeftGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToLeftGroup';
|
||||
static readonly LABEL = localize('splitEditorToLeftGroup', "Split Editor into Left Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'left', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToRightGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToRightGroup';
|
||||
static readonly LABEL = localize('splitEditorToRightGroup', "Split Editor into Right Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'right', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToFirstGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToFirstGroup';
|
||||
static readonly LABEL = localize('splitEditorToFirstGroup', "Split Editor into First Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'first', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplitEditorToLastGroupAction extends ExecuteCommandAction {
|
||||
|
||||
static readonly ID = 'workbench.action.splitEditorToLastGroup';
|
||||
static readonly LABEL = localize('splitEditorToLastGroup', "Split Editor into Last Group");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@ICommandService commandService: ICommandService
|
||||
) {
|
||||
super(id, label, COPY_ACTIVE_EDITOR_COMMAND_ID, commandService, { to: 'last', by: 'group' } as ActiveEditorMoveCopyArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import { EditorResolution, IEditorOptions, ITextEditorOptions } from 'vs/platfor
|
|||
import { Schemas } from 'vs/base/common/network';
|
||||
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
|
||||
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
|
||||
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
|
||||
|
@ -42,6 +43,7 @@ export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeGroup';
|
|||
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
|
||||
|
||||
export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
|
||||
export const COPY_ACTIVE_EDITOR_COMMAND_ID = 'copyActiveEditor';
|
||||
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
|
||||
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
|
||||
export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors';
|
||||
|
@ -87,13 +89,13 @@ export const API_OPEN_EDITOR_COMMAND_ID = '_workbench.open';
|
|||
export const API_OPEN_DIFF_EDITOR_COMMAND_ID = '_workbench.diff';
|
||||
export const API_OPEN_WITH_EDITOR_COMMAND_ID = '_workbench.openWith';
|
||||
|
||||
export interface ActiveEditorMoveArguments {
|
||||
export interface ActiveEditorMoveCopyArguments {
|
||||
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
|
||||
by: 'tab' | 'group';
|
||||
value: number;
|
||||
}
|
||||
|
||||
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
|
||||
const isActiveEditorMoveCopyArg = function (arg: ActiveEditorMoveCopyArguments): boolean {
|
||||
if (!isObject(arg)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -113,145 +115,174 @@ const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean
|
|||
return true;
|
||||
};
|
||||
|
||||
function registerActiveEditorMoveCommand(): void {
|
||||
function registerActiveEditorMoveCopyCommand(): void {
|
||||
|
||||
const moveCopyJSONSchema: IJSONSchema = {
|
||||
'type': 'object',
|
||||
'required': ['to'],
|
||||
'properties': {
|
||||
'to': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right']
|
||||
},
|
||||
'by': {
|
||||
'type': 'string',
|
||||
'enum': ['tab', 'group']
|
||||
},
|
||||
'value': {
|
||||
'type': 'number'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.editorTextFocus,
|
||||
primary: 0,
|
||||
handler: (accessor, args) => moveActiveEditor(args, accessor),
|
||||
handler: (accessor, args) => moveCopyActiveEditor(true, args, accessor),
|
||||
description: {
|
||||
description: localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"),
|
||||
args: [
|
||||
{
|
||||
name: localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
|
||||
description: localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
|
||||
constraint: isActiveEditorMoveArg,
|
||||
schema: {
|
||||
'type': 'object',
|
||||
'required': ['to'],
|
||||
'properties': {
|
||||
'to': {
|
||||
'type': 'string',
|
||||
'enum': ['left', 'right']
|
||||
},
|
||||
'by': {
|
||||
'type': 'string',
|
||||
'enum': ['tab', 'group']
|
||||
},
|
||||
'value': {
|
||||
'type': 'number'
|
||||
}
|
||||
},
|
||||
}
|
||||
constraint: isActiveEditorMoveCopyArg,
|
||||
schema: moveCopyJSONSchema
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null), accessor: ServicesAccessor): void {
|
||||
args.to = args.to || 'right';
|
||||
args.by = args.by || 'tab';
|
||||
args.value = typeof args.value === 'number' ? args.value : 1;
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: COPY_ACTIVE_EDITOR_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: EditorContextKeys.editorTextFocus,
|
||||
primary: 0,
|
||||
handler: (accessor, args) => moveCopyActiveEditor(false, args, accessor),
|
||||
description: {
|
||||
description: localize('editorCommand.activeEditorCopy.description', "Copy the active editor by groups"),
|
||||
args: [
|
||||
{
|
||||
name: localize('editorCommand.activeEditorCopy.arg.name', "Active editor copy argument"),
|
||||
description: localize('editorCommand.activeEditorCopy.arg.description', "Argument Properties:\n\t* 'to': String value providing where to copy.\n\t* 'value': Number value providing how many positions or an absolute position to copy."),
|
||||
constraint: isActiveEditorMoveCopyArg,
|
||||
schema: moveCopyJSONSchema
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
const activeEditorPane = accessor.get(IEditorService).activeEditorPane;
|
||||
if (activeEditorPane) {
|
||||
switch (args.by) {
|
||||
case 'tab':
|
||||
return moveActiveTab(args, activeEditorPane, accessor);
|
||||
case 'group':
|
||||
return moveActiveEditorToGroup(args, activeEditorPane, accessor);
|
||||
function moveCopyActiveEditor(isMove: boolean, args: ActiveEditorMoveCopyArguments = Object.create(null), accessor: ServicesAccessor): void {
|
||||
args.to = args.to || 'right';
|
||||
args.by = args.by || 'tab';
|
||||
args.value = typeof args.value === 'number' ? args.value : 1;
|
||||
|
||||
const activeEditorPane = accessor.get(IEditorService).activeEditorPane;
|
||||
if (activeEditorPane) {
|
||||
switch (args.by) {
|
||||
case 'tab':
|
||||
if (isMove) {
|
||||
return moveActiveTab(args, activeEditorPane);
|
||||
}
|
||||
break;
|
||||
case 'group':
|
||||
return moveCopyActiveEditorToGroup(isMove, args, activeEditorPane, accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function moveActiveTab(args: ActiveEditorMoveArguments, control: IVisibleEditorPane, accessor: ServicesAccessor): void {
|
||||
const group = control.group;
|
||||
let index = group.getIndexOfEditor(control.input);
|
||||
switch (args.to) {
|
||||
case 'first':
|
||||
index = 0;
|
||||
break;
|
||||
case 'last':
|
||||
index = group.count - 1;
|
||||
break;
|
||||
case 'left':
|
||||
index = index - args.value;
|
||||
break;
|
||||
case 'right':
|
||||
index = index + args.value;
|
||||
break;
|
||||
case 'center':
|
||||
index = Math.round(group.count / 2) - 1;
|
||||
break;
|
||||
case 'position':
|
||||
index = args.value - 1;
|
||||
break;
|
||||
function moveActiveTab(args: ActiveEditorMoveCopyArguments, control: IVisibleEditorPane): void {
|
||||
const group = control.group;
|
||||
let index = group.getIndexOfEditor(control.input);
|
||||
switch (args.to) {
|
||||
case 'first':
|
||||
index = 0;
|
||||
break;
|
||||
case 'last':
|
||||
index = group.count - 1;
|
||||
break;
|
||||
case 'left':
|
||||
index = index - args.value;
|
||||
break;
|
||||
case 'right':
|
||||
index = index + args.value;
|
||||
break;
|
||||
case 'center':
|
||||
index = Math.round(group.count / 2) - 1;
|
||||
break;
|
||||
case 'position':
|
||||
index = args.value - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
|
||||
group.moveEditor(control.input, group, { index });
|
||||
}
|
||||
|
||||
index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
|
||||
group.moveEditor(control.input, group, { index });
|
||||
}
|
||||
function moveCopyActiveEditorToGroup(isMove: boolean, args: ActiveEditorMoveCopyArguments, control: IVisibleEditorPane, accessor: ServicesAccessor): void {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
|
||||
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisibleEditorPane, accessor: ServicesAccessor): void {
|
||||
const editorGroupService = accessor.get(IEditorGroupsService);
|
||||
const configurationService = accessor.get(IConfigurationService);
|
||||
const sourceGroup = control.group;
|
||||
let targetGroup: IEditorGroup | undefined;
|
||||
|
||||
const sourceGroup = control.group;
|
||||
let targetGroup: IEditorGroup | undefined;
|
||||
switch (args.to) {
|
||||
case 'left':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.LEFT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.LEFT);
|
||||
}
|
||||
break;
|
||||
case 'right':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.RIGHT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.RIGHT);
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.UP }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.UP);
|
||||
}
|
||||
break;
|
||||
case 'down':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.DOWN }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.DOWN);
|
||||
}
|
||||
break;
|
||||
case 'first':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.FIRST }, sourceGroup);
|
||||
break;
|
||||
case 'last':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.LAST }, sourceGroup);
|
||||
break;
|
||||
case 'previous':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.PREVIOUS }, sourceGroup);
|
||||
break;
|
||||
case 'next':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.NEXT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, preferredSideBySideGroupDirection(configurationService));
|
||||
}
|
||||
break;
|
||||
case 'center':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[(editorGroupService.count / 2) - 1];
|
||||
break;
|
||||
case 'position':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[args.value - 1];
|
||||
break;
|
||||
}
|
||||
|
||||
switch (args.to) {
|
||||
case 'left':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.LEFT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.LEFT);
|
||||
if (targetGroup) {
|
||||
if (isMove) {
|
||||
sourceGroup.moveEditor(control.input, targetGroup);
|
||||
} else if (sourceGroup.id !== targetGroup.id) {
|
||||
sourceGroup.copyEditor(control.input, targetGroup);
|
||||
}
|
||||
break;
|
||||
case 'right':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.RIGHT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.RIGHT);
|
||||
}
|
||||
break;
|
||||
case 'up':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.UP }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.UP);
|
||||
}
|
||||
break;
|
||||
case 'down':
|
||||
targetGroup = editorGroupService.findGroup({ direction: GroupDirection.DOWN }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.DOWN);
|
||||
}
|
||||
break;
|
||||
case 'first':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.FIRST }, sourceGroup);
|
||||
break;
|
||||
case 'last':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.LAST }, sourceGroup);
|
||||
break;
|
||||
case 'previous':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.PREVIOUS }, sourceGroup);
|
||||
break;
|
||||
case 'next':
|
||||
targetGroup = editorGroupService.findGroup({ location: GroupLocation.NEXT }, sourceGroup);
|
||||
if (!targetGroup) {
|
||||
targetGroup = editorGroupService.addGroup(sourceGroup, preferredSideBySideGroupDirection(configurationService));
|
||||
}
|
||||
break;
|
||||
case 'center':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[(editorGroupService.count / 2) - 1];
|
||||
break;
|
||||
case 'position':
|
||||
targetGroup = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE)[args.value - 1];
|
||||
break;
|
||||
}
|
||||
|
||||
if (targetGroup) {
|
||||
sourceGroup.moveEditor(control.input, targetGroup);
|
||||
targetGroup.focus();
|
||||
targetGroup.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1388,7 +1419,7 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
|
|||
}
|
||||
|
||||
export function setup(): void {
|
||||
registerActiveEditorMoveCommand();
|
||||
registerActiveEditorMoveCopyCommand();
|
||||
registerEditorGroupsLayoutCommand();
|
||||
registerDiffEditorCommands();
|
||||
registerOpenEditorAPICommands();
|
||||
|
|
|
@ -155,7 +155,7 @@ export abstract class BaseEditorQuickAccessProvider extends PickerQuickAccessPro
|
|||
|
||||
return isDirty ? localize('entryAriaLabelDirty', "{0}, dirty", nameAndDescription) : nameAndDescription;
|
||||
})(),
|
||||
description: editor.getDescription(),
|
||||
description,
|
||||
iconClasses: getIconClasses(this.modelService, this.modeService, resource).concat(editor.getLabelExtraClasses()),
|
||||
italic: !this.editorGroupService.getGroup(groupId)?.isPinned(editor),
|
||||
buttons: (() => {
|
||||
|
|
|
@ -78,13 +78,13 @@ export class NoTabsTitleControl extends TitleControl {
|
|||
this._register(addDisposableListener(titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleTap(e)));
|
||||
|
||||
// Context Menu
|
||||
[EventType.CONTEXT_MENU, TouchEventType.Contextmenu].forEach(event => {
|
||||
for (const event of [EventType.CONTEXT_MENU, TouchEventType.Contextmenu]) {
|
||||
this._register(addDisposableListener(titleContainer, event, e => {
|
||||
if (this.group.activeEditor) {
|
||||
this.onContextMenu(this.group.activeEditor, e, titleContainer);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onTitleLabelClick(e: MouseEvent): void {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import 'vs/css!./media/tabstitlecontrol';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { shorten } from 'vs/base/common/labels';
|
||||
import { EditorResourceAccessor, GroupIdentifier, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor';
|
||||
import { EditorResourceAccessor, GroupIdentifier, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities } from 'vs/workbench/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { computeEditorAriaLabel } from 'vs/workbench/browser/editor';
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
@ -40,7 +40,7 @@ import { CloseOneEditorAction, UnpinEditorAction } from 'vs/workbench/browser/pa
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { withNullAsUndefined, assertAllDefined, assertIsDefined } from 'vs/base/common/types';
|
||||
import { assertAllDefined, assertIsDefined } from 'vs/base/common/types';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
@ -56,11 +56,12 @@ import { UNLOCK_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/edito
|
|||
interface IEditorInputLabel {
|
||||
name?: string;
|
||||
description?: string;
|
||||
forceDescription?: boolean;
|
||||
title?: string;
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
type AugmentedLabel = IEditorInputLabel & { editor: EditorInput };
|
||||
type IEditorInputLabelAndEditor = IEditorInputLabel & { editor: EditorInput };
|
||||
|
||||
export class TabsTitleControl extends TitleControl {
|
||||
|
||||
|
@ -218,7 +219,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
}));
|
||||
|
||||
// New file when double clicking on tabs container (but not tabs)
|
||||
[TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => {
|
||||
for (const eventType of [TouchEventType.Tap, EventType.DBLCLICK]) {
|
||||
this._register(addDisposableListener(tabsContainer, eventType, (e: MouseEvent | GestureEvent) => {
|
||||
if (eventType === EventType.DBLCLICK) {
|
||||
if (e.target !== tabsContainer) {
|
||||
|
@ -245,7 +246,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
}
|
||||
}, this.group.id);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Prevent auto-scrolling (https://github.com/microsoft/vscode/issues/16690)
|
||||
this._register(addDisposableListener(tabsContainer, EventType.MOUSE_DOWN, e => {
|
||||
|
@ -784,7 +785,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
}));
|
||||
|
||||
// Double click: either pin or toggle maximized
|
||||
[TouchEventType.Tap, EventType.DBLCLICK].forEach(eventType => {
|
||||
for (const eventType of [TouchEventType.Tap, EventType.DBLCLICK]) {
|
||||
disposables.add(addDisposableListener(tab, eventType, (e: MouseEvent | GestureEvent) => {
|
||||
if (eventType === EventType.DBLCLICK) {
|
||||
EventHelper.stop(e);
|
||||
|
@ -799,7 +800,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
this.group.pinEditor(editor);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Context menu
|
||||
disposables.add(addDisposableListener(tab, EventType.CONTEXT_MENU, e => {
|
||||
|
@ -953,11 +954,12 @@ export class TabsTitleControl extends TitleControl {
|
|||
const { verbosity, shortenDuplicates } = this.getLabelConfigFlags(labelFormat);
|
||||
|
||||
// Build labels and descriptions for each editor
|
||||
const labels = this.group.editors.map((editor, index) => ({
|
||||
const labels: IEditorInputLabelAndEditor[] = this.group.editors.map((editor, index) => ({
|
||||
editor,
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription(verbosity),
|
||||
title: withNullAsUndefined(editor.getTitle(Verbosity.LONG)),
|
||||
forceDescription: editor.hasCapability(EditorInputCapabilities.ForceDescription),
|
||||
title: editor.getTitle(Verbosity.LONG),
|
||||
ariaLabel: computeEditorAriaLabel(editor, index, this.group, this.editorGroupService.count)
|
||||
}));
|
||||
|
||||
|
@ -969,63 +971,68 @@ export class TabsTitleControl extends TitleControl {
|
|||
this.tabLabels = labels;
|
||||
}
|
||||
|
||||
private shortenTabLabels(labels: AugmentedLabel[]): void {
|
||||
private shortenTabLabels(labels: IEditorInputLabelAndEditor[]): void {
|
||||
|
||||
// Gather duplicate titles, while filtering out invalid descriptions
|
||||
const mapTitleToDuplicates = new Map<string, AugmentedLabel[]>();
|
||||
const mapNameToDuplicates = new Map<string, IEditorInputLabelAndEditor[]>();
|
||||
for (const label of labels) {
|
||||
if (typeof label.description === 'string') {
|
||||
getOrSet(mapTitleToDuplicates, label.name, []).push(label);
|
||||
getOrSet(mapNameToDuplicates, label.name, []).push(label);
|
||||
} else {
|
||||
label.description = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Identify duplicate titles and shorten descriptions
|
||||
mapTitleToDuplicates.forEach(duplicateTitles => {
|
||||
// Identify duplicate names and shorten descriptions
|
||||
for (const [, duplicateLabels] of mapNameToDuplicates) {
|
||||
|
||||
// Remove description if the title isn't duplicated
|
||||
if (duplicateTitles.length === 1) {
|
||||
duplicateTitles[0].description = '';
|
||||
// and we have no indication to enforce description
|
||||
if (duplicateLabels.length === 1 && !duplicateLabels[0].forceDescription) {
|
||||
duplicateLabels[0].description = '';
|
||||
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Identify duplicate descriptions
|
||||
const mapDescriptionToDuplicates = new Map<string, AugmentedLabel[]>();
|
||||
for (const label of duplicateTitles) {
|
||||
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
|
||||
const mapDescriptionToDuplicates = new Map<string, IEditorInputLabelAndEditor[]>();
|
||||
for (const duplicateLabel of duplicateLabels) {
|
||||
getOrSet(mapDescriptionToDuplicates, duplicateLabel.description, []).push(duplicateLabel);
|
||||
}
|
||||
|
||||
// For editors with duplicate descriptions, check whether any long descriptions differ
|
||||
let useLongDescriptions = false;
|
||||
mapDescriptionToDuplicates.forEach((duplicateDescriptions, name) => {
|
||||
if (!useLongDescriptions && duplicateDescriptions.length > 1) {
|
||||
const [first, ...rest] = duplicateDescriptions.map(({ editor }) => editor.getDescription(Verbosity.LONG));
|
||||
for (const [, duplicateLabels] of mapDescriptionToDuplicates) {
|
||||
if (!useLongDescriptions && duplicateLabels.length > 1) {
|
||||
const [first, ...rest] = duplicateLabels.map(({ editor }) => editor.getDescription(Verbosity.LONG));
|
||||
useLongDescriptions = rest.some(description => description !== first);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// If so, replace all descriptions with long descriptions
|
||||
if (useLongDescriptions) {
|
||||
mapDescriptionToDuplicates.clear();
|
||||
duplicateTitles.forEach(label => {
|
||||
label.description = label.editor.getDescription(Verbosity.LONG);
|
||||
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
|
||||
});
|
||||
for (const duplicateLabel of duplicateLabels) {
|
||||
duplicateLabel.description = duplicateLabel.editor.getDescription(Verbosity.LONG);
|
||||
getOrSet(mapDescriptionToDuplicates, duplicateLabel.description, []).push(duplicateLabel);
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain final set of descriptions
|
||||
const descriptions: string[] = [];
|
||||
mapDescriptionToDuplicates.forEach((_, description) => descriptions.push(description));
|
||||
for (const [description] of mapDescriptionToDuplicates) {
|
||||
descriptions.push(description);
|
||||
}
|
||||
|
||||
// Remove description if all descriptions are identical
|
||||
// Remove description if all descriptions are identical unless forced
|
||||
if (descriptions.length === 1) {
|
||||
for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) {
|
||||
label.description = '';
|
||||
if (!label.forceDescription) {
|
||||
label.description = '';
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Shorten descriptions
|
||||
|
@ -1035,7 +1042,7 @@ export class TabsTitleControl extends TitleControl {
|
|||
label.description = shortenedDescriptions[index];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getLabelConfigFlags(value: string | undefined) {
|
||||
|
@ -1096,21 +1103,21 @@ export class TabsTitleControl extends TitleControl {
|
|||
|
||||
// Settings
|
||||
const tabActionsVisibility = isTabSticky && options.pinnedTabSizing === 'compact' ? 'off' /* treat sticky compact tabs as tabCloseButton: 'off' */ : options.tabCloseButton;
|
||||
['off', 'left', 'right'].forEach(option => {
|
||||
for (const option of ['off', 'left', 'right']) {
|
||||
tabContainer.classList.toggle(`tab-actions-${option}`, tabActionsVisibility === option);
|
||||
});
|
||||
}
|
||||
|
||||
const tabSizing = isTabSticky && options.pinnedTabSizing === 'shrink' ? 'shrink' /* treat sticky shrink tabs as tabSizing: 'shrink' */ : options.tabSizing;
|
||||
['fit', 'shrink'].forEach(option => {
|
||||
for (const option of ['fit', 'shrink']) {
|
||||
tabContainer.classList.toggle(`sizing-${option}`, tabSizing === option);
|
||||
});
|
||||
}
|
||||
|
||||
tabContainer.classList.toggle('has-icon', options.showIcons && options.hasIcons);
|
||||
|
||||
tabContainer.classList.toggle('sticky', isTabSticky);
|
||||
['normal', 'compact', 'shrink'].forEach(option => {
|
||||
for (const option of ['normal', 'compact', 'shrink']) {
|
||||
tabContainer.classList.toggle(`sticky-${option}`, isTabSticky && options.pinnedTabSizing === option);
|
||||
});
|
||||
}
|
||||
|
||||
// Sticky compact/shrink tabs need a position to remain at their location
|
||||
// when scrolling to stay in view (requirement for position: sticky)
|
||||
|
|
|
@ -44,6 +44,10 @@ export class StatusbarEntryItem extends Disposable {
|
|||
return assertIsDefined(this.entry).name;
|
||||
}
|
||||
|
||||
get hasCommand(): boolean {
|
||||
return typeof this.entry?.command !== 'undefined';
|
||||
}
|
||||
|
||||
constructor(
|
||||
private container: HTMLElement,
|
||||
entry: IStatusbarEntry,
|
||||
|
|
|
@ -63,6 +63,7 @@ export function isStatusbarEntryLocation(thing: unknown): thing is IStatusbarEnt
|
|||
export interface IStatusbarViewModelEntry {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly hasCommand: boolean;
|
||||
readonly alignment: StatusbarAlignment;
|
||||
readonly priority: IStatusbarEntryPriority;
|
||||
readonly container: HTMLElement;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import 'vs/css!./media/statusbarpart';
|
||||
import { localize } from 'vs/nls';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, dispose, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Part } from 'vs/workbench/browser/part';
|
||||
import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
@ -95,6 +95,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
}
|
||||
}(this.configurationService, this.hoverService);
|
||||
|
||||
private readonly compactEntriesDisposable = this._register(new MutableDisposable<DisposableStore>());
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
|
@ -114,7 +116,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
private registerListeners(): void {
|
||||
|
||||
// Entry visibility changes
|
||||
this._register(this.onDidChangeEntryVisibility(() => this.updateClasses()));
|
||||
this._register(this.onDidChangeEntryVisibility(() => this.updateCompactEntries()));
|
||||
|
||||
// Workbench state changes
|
||||
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateStyles()));
|
||||
|
@ -176,6 +178,7 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
readonly labelContainer = item.labelContainer;
|
||||
|
||||
get name() { return item.name; }
|
||||
get hasCommand() { return item.hasCommand; }
|
||||
};
|
||||
|
||||
// Add to view model
|
||||
|
@ -337,8 +340,8 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
target.appendChild(entry.container);
|
||||
}
|
||||
|
||||
// Update CSS classes
|
||||
this.updateClasses();
|
||||
// Update compact entries
|
||||
this.updateCompactEntries();
|
||||
}
|
||||
|
||||
private appendStatusbarEntry(entry: IStatusbarViewModelEntry): void {
|
||||
|
@ -357,14 +360,14 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
target.insertBefore(entry.container, entries[index + 1].container); // insert before next element otherwise
|
||||
}
|
||||
|
||||
// Update CSS classes
|
||||
this.updateClasses();
|
||||
// Update compact entries
|
||||
this.updateCompactEntries();
|
||||
}
|
||||
|
||||
private updateClasses(): void {
|
||||
private updateCompactEntries(): void {
|
||||
const entries = this.viewModel.entries;
|
||||
|
||||
// Clear compact related CSS classes if any
|
||||
// Find visible entries and clear compact related CSS classes if any
|
||||
const mapIdToVisibleEntry = new Map<string, IStatusbarViewModelEntry>();
|
||||
for (const entry of entries) {
|
||||
if (!this.viewModel.isHidden(entry.id)) {
|
||||
|
@ -374,21 +377,58 @@ export class StatusbarPart extends Part implements IStatusbarService {
|
|||
entry.container.classList.remove('compact-left', 'compact-right');
|
||||
}
|
||||
|
||||
// Update entries with compact related CSS classes as needed
|
||||
// Figure out groups of entries with `compact` alignment
|
||||
const compactEntryGroups = new Map<string, Set<IStatusbarViewModelEntry>>();
|
||||
for (const entry of mapIdToVisibleEntry.values()) {
|
||||
if (
|
||||
isStatusbarEntryLocation(entry.priority.primary) && // entry references another entry as location
|
||||
entry.priority.primary.compact // entry wants to be compact
|
||||
) {
|
||||
const location = mapIdToVisibleEntry.get(entry.priority.primary.id);
|
||||
if (location) {
|
||||
if (entry.priority.primary.alignment === StatusbarAlignment.LEFT) {
|
||||
location.container.classList.add('compact-left');
|
||||
entry.container.classList.add('compact-right');
|
||||
} else {
|
||||
location.container.classList.add('compact-right');
|
||||
entry.container.classList.add('compact-left');
|
||||
const locationId = entry.priority.primary.id;
|
||||
const location = mapIdToVisibleEntry.get(locationId);
|
||||
if (!location) {
|
||||
continue; // skip if location does not exist
|
||||
}
|
||||
|
||||
// Build a map of entries that are compact among each other
|
||||
let compactEntryGroup = compactEntryGroups.get(locationId);
|
||||
if (!compactEntryGroup) {
|
||||
compactEntryGroup = new Set<IStatusbarViewModelEntry>([entry, location]);
|
||||
compactEntryGroups.set(locationId, compactEntryGroup);
|
||||
} else {
|
||||
compactEntryGroup.add(entry);
|
||||
}
|
||||
|
||||
// Adjust CSS classes to move compact items closer together
|
||||
if (entry.priority.primary.alignment === StatusbarAlignment.LEFT) {
|
||||
location.container.classList.add('compact-left');
|
||||
entry.container.classList.add('compact-right');
|
||||
} else {
|
||||
location.container.classList.add('compact-right');
|
||||
entry.container.classList.add('compact-left');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Install mouse listeners to update hover feedback for
|
||||
// all compact entries that belong to each other
|
||||
const statusBarItemHoverBackground = this.getColor(STATUS_BAR_ITEM_HOVER_BACKGROUND)?.toString();
|
||||
this.compactEntriesDisposable.value = new DisposableStore();
|
||||
if (statusBarItemHoverBackground && this.theme.type !== ColorScheme.HIGH_CONTRAST) {
|
||||
for (const [, compactEntryGroup] of compactEntryGroups) {
|
||||
for (const compactEntry of compactEntryGroup) {
|
||||
if (!compactEntry.hasCommand) {
|
||||
continue; // only show hover feedback when we have a command
|
||||
}
|
||||
|
||||
this.compactEntriesDisposable.value.add(addDisposableListener(compactEntry.labelContainer, EventType.MOUSE_OVER, () => {
|
||||
compactEntryGroup.forEach(compactEntry => compactEntry.labelContainer.style.backgroundColor = statusBarItemHoverBackground);
|
||||
}));
|
||||
|
||||
this.compactEntriesDisposable.value.add(addDisposableListener(compactEntry.labelContainer, EventType.MOUSE_OUT, () => {
|
||||
compactEntryGroup.forEach(compactEntry => compactEntry.labelContainer.style.backgroundColor = '');
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -502,7 +542,8 @@ registerThemingParticipant((theme, collector) => {
|
|||
|
||||
const statusBarItemActiveBackground = theme.getColor(STATUS_BAR_ITEM_ACTIVE_BACKGROUND);
|
||||
if (statusBarItemActiveBackground) {
|
||||
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active:not(.disabled) { background-color: ${statusBarItemActiveBackground}; }`);
|
||||
// using !important for this rule to win over any background color that is set via JS code for compact items in a group
|
||||
collector.addRule(`.monaco-workbench .part.statusbar > .items-container > .statusbar-item a:active:not(.disabled) { background-color: ${statusBarItemActiveBackground} !important; }`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -522,7 +522,15 @@ export const enum EditorInputCapabilities {
|
|||
* Signals that the editor can split into 2 in the same
|
||||
* editor group.
|
||||
*/
|
||||
CanSplitInGroup = 1 << 5
|
||||
CanSplitInGroup = 1 << 5,
|
||||
|
||||
/**
|
||||
* Signals that the editor wants it's description to be
|
||||
* visible when presented to the user. By default, a UI
|
||||
* component may decide to hide the description portion
|
||||
* for brevity.
|
||||
*/
|
||||
ForceDescription = 1 << 6
|
||||
}
|
||||
|
||||
export type IUntypedEditorInput = IResourceEditorInput | ITextResourceEditorInput | IUntitledTextResourceEditorInput | IResourceDiffEditorInput | IResourceSideBySideEditorInput;
|
||||
|
|
|
@ -3,22 +3,32 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { AbstractSideBySideEditorInputSerializer, SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
||||
import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, GroupIdentifier, IResourceDiffEditorInput, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput } from 'vs/workbench/common/editor';
|
||||
import { TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID, Verbosity, IEditorDescriptor, IEditorPane, GroupIdentifier, IResourceDiffEditorInput, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, isResourceDiffEditorInput, IDiffEditorInput, IResourceSideBySideEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor';
|
||||
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
|
||||
import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel';
|
||||
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { shorten } from 'vs/base/common/labels';
|
||||
|
||||
interface IDiffEditorInputLabels {
|
||||
name: string;
|
||||
|
||||
shortDescription: string | undefined;
|
||||
mediumDescription: string | undefined;
|
||||
longDescription: string | undefined;
|
||||
|
||||
forceDescription: boolean;
|
||||
|
||||
shortTitle: string;
|
||||
mediumTitle: string;
|
||||
longTitle: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base editor input for the diff editor. It is made up of two editor inputs, the original version
|
||||
|
@ -36,66 +46,119 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito
|
|||
return DEFAULT_EDITOR_ASSOCIATION.id;
|
||||
}
|
||||
|
||||
override get capabilities(): EditorInputCapabilities {
|
||||
let capabilities = super.capabilities;
|
||||
|
||||
// Force description capability depends on labels
|
||||
if (this.labels.forceDescription) {
|
||||
capabilities |= EditorInputCapabilities.ForceDescription;
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
private cachedModel: DiffEditorModel | undefined = undefined;
|
||||
|
||||
private readonly labels = this.computeLabels();
|
||||
|
||||
constructor(
|
||||
name: string | undefined,
|
||||
description: string | undefined,
|
||||
preferredName: string | undefined,
|
||||
preferredDescription: string | undefined,
|
||||
readonly original: EditorInput,
|
||||
readonly modified: EditorInput,
|
||||
private readonly forceOpenAsBinary: boolean | undefined,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IEditorService editorService: IEditorService
|
||||
) {
|
||||
super(name, description, original, modified, editorService);
|
||||
super(preferredName, preferredDescription, original, modified, editorService);
|
||||
}
|
||||
|
||||
private computeLabels(): IDiffEditorInputLabels {
|
||||
|
||||
// Name
|
||||
let name: string;
|
||||
let forceDescription = false;
|
||||
if (this.preferredName) {
|
||||
name = this.preferredName;
|
||||
} else {
|
||||
const originalName = this.original.getName();
|
||||
const modifiedName = this.modified.getName();
|
||||
|
||||
name = localize('sideBySideLabels', "{0} ↔ {1}", originalName, modifiedName);
|
||||
|
||||
// Enforce description when the names are identical
|
||||
forceDescription = originalName === modifiedName;
|
||||
}
|
||||
|
||||
// Description
|
||||
let shortDescription: string | undefined;
|
||||
let mediumDescription: string | undefined;
|
||||
let longDescription: string | undefined;
|
||||
if (this.preferredDescription) {
|
||||
shortDescription = this.preferredDescription;
|
||||
mediumDescription = this.preferredDescription;
|
||||
longDescription = this.preferredDescription;
|
||||
} else {
|
||||
shortDescription = this.computeLabel(this.original.getDescription(Verbosity.SHORT), this.modified.getDescription(Verbosity.SHORT));
|
||||
longDescription = this.computeLabel(this.original.getDescription(Verbosity.LONG), this.modified.getDescription(Verbosity.LONG));
|
||||
|
||||
// Medium Description: try to be verbose by computing
|
||||
// a label that resembles the difference between the two
|
||||
const originalMediumDescription = this.original.getDescription(Verbosity.MEDIUM);
|
||||
const modifiedMediumDescription = this.modified.getDescription(Verbosity.MEDIUM);
|
||||
if (originalMediumDescription && modifiedMediumDescription) {
|
||||
const [shortenedOriginalMediumDescription, shortenedModifiedMediumDescription] = shorten([originalMediumDescription, modifiedMediumDescription]);
|
||||
mediumDescription = this.computeLabel(shortenedOriginalMediumDescription, shortenedModifiedMediumDescription);
|
||||
}
|
||||
}
|
||||
|
||||
// Title
|
||||
const shortTitle = this.computeLabel(this.original.getTitle(Verbosity.SHORT) ?? this.original.getName(), this.modified.getTitle(Verbosity.SHORT) ?? this.modified.getName(), ' ↔ ');
|
||||
const mediumTitle = this.computeLabel(this.original.getTitle(Verbosity.MEDIUM) ?? this.original.getName(), this.modified.getTitle(Verbosity.MEDIUM) ?? this.modified.getName(), ' ↔ ');
|
||||
const longTitle = this.computeLabel(this.original.getTitle(Verbosity.LONG) ?? this.original.getName(), this.modified.getTitle(Verbosity.LONG) ?? this.modified.getName(), ' ↔ ');
|
||||
|
||||
return { name, shortDescription, mediumDescription, longDescription, forceDescription, shortTitle, mediumTitle, longTitle };
|
||||
}
|
||||
|
||||
private computeLabel(originalLabel: string, modifiedLabel: string, separator?: string): string;
|
||||
private computeLabel(originalLabel: string | undefined, modifiedLabel: string | undefined, separator?: string): string | undefined;
|
||||
private computeLabel(originalLabel: string | undefined, modifiedLabel: string | undefined, separator = ' - '): string | undefined {
|
||||
if (!originalLabel || !modifiedLabel) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (originalLabel === modifiedLabel) {
|
||||
return modifiedLabel;
|
||||
}
|
||||
|
||||
return `${originalLabel}${separator}${modifiedLabel}`;
|
||||
}
|
||||
|
||||
override getName(): string {
|
||||
if (!this.name) {
|
||||
|
||||
// Craft a name from original and modified input that includes the
|
||||
// relative path in case both sides have different parents and we
|
||||
// compare file resources.
|
||||
const fileResources = this.asFileResources();
|
||||
if (fileResources && dirname(fileResources.original).path !== dirname(fileResources.modified).path) {
|
||||
return `${this.labelService.getUriLabel(fileResources.original, { relative: true })} ↔ ${this.labelService.getUriLabel(fileResources.modified, { relative: true })}`;
|
||||
}
|
||||
|
||||
return localize('sideBySideLabels', "{0} ↔ {1}", this.original.getName(), this.modified.getName());
|
||||
}
|
||||
|
||||
return this.name;
|
||||
return this.labels.name;
|
||||
}
|
||||
|
||||
override getDescription(verbosity = Verbosity.MEDIUM): string | undefined {
|
||||
if (typeof this.description !== 'string') {
|
||||
|
||||
// Pass the description of the modified side in case both original
|
||||
// and modified input have the same parent and we compare file resources.
|
||||
const fileResources = this.asFileResources();
|
||||
if (fileResources && dirname(fileResources.original).path === dirname(fileResources.modified).path) {
|
||||
return this.modified.getDescription(verbosity);
|
||||
}
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.labels.shortDescription;
|
||||
case Verbosity.LONG:
|
||||
return this.labels.longDescription;
|
||||
case Verbosity.MEDIUM:
|
||||
default:
|
||||
return this.labels.mediumDescription;
|
||||
}
|
||||
|
||||
return this.description;
|
||||
}
|
||||
|
||||
private asFileResources(): { original: URI, modified: URI } | undefined {
|
||||
if (
|
||||
this.original instanceof AbstractTextResourceEditorInput &&
|
||||
this.modified instanceof AbstractTextResourceEditorInput &&
|
||||
this.fileService.canHandleResource(this.original.preferredResource) &&
|
||||
this.fileService.canHandleResource(this.modified.preferredResource)
|
||||
) {
|
||||
return {
|
||||
original: this.original.preferredResource,
|
||||
modified: this.modified.preferredResource
|
||||
};
|
||||
override getTitle(verbosity?: Verbosity): string {
|
||||
switch (verbosity) {
|
||||
case Verbosity.SHORT:
|
||||
return this.labels.shortTitle;
|
||||
case Verbosity.LONG:
|
||||
return this.labels.longTitle;
|
||||
default:
|
||||
case Verbosity.MEDIUM:
|
||||
return this.labels.mediumTitle;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
override async resolve(): Promise<EditorModel> {
|
||||
|
@ -184,7 +247,7 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito
|
|||
|
||||
export class DiffEditorInputSerializer extends AbstractSideBySideEditorInputSerializer {
|
||||
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,13 +99,6 @@ export abstract class EditorInput extends AbstractEditorInput {
|
|||
return `Editor ${this.typeId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra classes to apply to the label of this input.
|
||||
*/
|
||||
getLabelExtraClasses(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display description of this input.
|
||||
*/
|
||||
|
@ -120,6 +113,13 @@ export abstract class EditorInput extends AbstractEditorInput {
|
|||
return this.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra classes to apply to the label of this input.
|
||||
*/
|
||||
getLabelExtraClasses(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the aria label to be read out by a screen reader.
|
||||
*/
|
||||
|
|
|
@ -60,8 +60,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
private hasIdenticalSides = this.primary.matches(this.secondary);
|
||||
|
||||
constructor(
|
||||
protected readonly name: string | undefined,
|
||||
protected readonly description: string | undefined,
|
||||
protected readonly preferredName: string | undefined,
|
||||
protected readonly preferredDescription: string | undefined,
|
||||
readonly secondary: EditorInput,
|
||||
readonly primary: EditorInput,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
|
@ -91,31 +91,37 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
}
|
||||
|
||||
override getName(): string {
|
||||
if (!this.name) {
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getName(); // keep name concise when same editor is opened side by side
|
||||
}
|
||||
|
||||
return localize('sideBySideLabels', "{0} - {1}", this.secondary.getName(), this.primary.getName());
|
||||
const preferredName = this.getPreferredName();
|
||||
if (preferredName) {
|
||||
return preferredName;
|
||||
}
|
||||
|
||||
return this.name;
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getName(); // keep name concise when same editor is opened side by side
|
||||
}
|
||||
|
||||
return localize('sideBySideLabels', "{0} - {1}", this.secondary.getName(), this.primary.getName());
|
||||
}
|
||||
|
||||
override getLabelExtraClasses(): string[] {
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getLabelExtraClasses();
|
||||
}
|
||||
|
||||
return super.getLabelExtraClasses();
|
||||
getPreferredName(): string | undefined {
|
||||
return this.preferredName;
|
||||
}
|
||||
|
||||
override getDescription(verbosity?: Verbosity): string | undefined {
|
||||
const preferredDescription = this.getPreferredDescription();
|
||||
if (preferredDescription) {
|
||||
return preferredDescription;
|
||||
}
|
||||
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getDescription(verbosity);
|
||||
}
|
||||
|
||||
return this.description;
|
||||
return super.getDescription(verbosity);
|
||||
}
|
||||
|
||||
getPreferredDescription(): string | undefined {
|
||||
return this.preferredDescription;
|
||||
}
|
||||
|
||||
override getTitle(verbosity?: Verbosity): string {
|
||||
|
@ -126,6 +132,14 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
return super.getTitle(verbosity);
|
||||
}
|
||||
|
||||
override getLabelExtraClasses(): string[] {
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getLabelExtraClasses();
|
||||
}
|
||||
|
||||
return super.getLabelExtraClasses();
|
||||
}
|
||||
|
||||
override getAriaLabel(): string {
|
||||
if (this.hasIdenticalSides) {
|
||||
return this.primary.getAriaLabel();
|
||||
|
@ -154,7 +168,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
return editor;
|
||||
}
|
||||
|
||||
return new SideBySideEditorInput(this.name, this.description, editor, editor, this.editorService);
|
||||
return new SideBySideEditorInput(this.preferredName, this.preferredDescription, editor, editor, this.editorService);
|
||||
}
|
||||
|
||||
override async saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<EditorInput | undefined> {
|
||||
|
@ -163,7 +177,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
return editor;
|
||||
}
|
||||
|
||||
return new SideBySideEditorInput(this.name, this.description, editor, editor, this.editorService);
|
||||
return new SideBySideEditorInput(this.preferredName, this.preferredDescription, editor, editor, this.editorService);
|
||||
}
|
||||
|
||||
override revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
|
||||
|
@ -185,7 +199,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
|
||||
if (isEditorInput(renameResult.editor)) {
|
||||
return {
|
||||
editor: new SideBySideEditorInput(this.name, this.description, renameResult.editor, renameResult.editor, this.editorService),
|
||||
editor: new SideBySideEditorInput(this.preferredName, this.preferredDescription, renameResult.editor, renameResult.editor, this.editorService),
|
||||
options: {
|
||||
...renameResult.options,
|
||||
viewState: findViewStateForEditor(this, group, this.editorService)
|
||||
|
@ -196,8 +210,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
if (isResourceEditorInput(renameResult.editor)) {
|
||||
return {
|
||||
editor: {
|
||||
label: this.name,
|
||||
description: this.description,
|
||||
label: this.preferredName,
|
||||
description: this.preferredDescription,
|
||||
primary: renameResult.editor,
|
||||
secondary: renameResult.editor,
|
||||
options: {
|
||||
|
@ -222,8 +236,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
!isResourceSideBySideEditorInput(primaryResourceEditorInput) && !isResourceSideBySideEditorInput(secondaryResourceEditorInput)
|
||||
) {
|
||||
const untypedInput: IResourceSideBySideEditorInput = {
|
||||
label: this.name,
|
||||
description: this.description,
|
||||
label: this.preferredName,
|
||||
description: this.preferredDescription,
|
||||
primary: primaryResourceEditorInput,
|
||||
secondary: secondaryResourceEditorInput
|
||||
};
|
||||
|
@ -263,7 +277,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi
|
|||
|
||||
// Register SideBySide/DiffEditor Input Serializer
|
||||
interface ISerializedSideBySideEditorInput {
|
||||
name: string;
|
||||
name: string | undefined;
|
||||
description: string | undefined;
|
||||
|
||||
primarySerialized: string;
|
||||
|
@ -298,8 +312,8 @@ export abstract class AbstractSideBySideEditorInputSerializer implements IEditor
|
|||
|
||||
if (primarySerialized && secondarySerialized) {
|
||||
const serializedEditorInput: ISerializedSideBySideEditorInput = {
|
||||
name: input.getName(),
|
||||
description: input.getDescription(),
|
||||
name: input.getPreferredName(),
|
||||
description: input.getPreferredDescription(),
|
||||
primarySerialized: primarySerialized,
|
||||
secondarySerialized: secondarySerialized,
|
||||
primaryTypeId: input.primary.typeId,
|
||||
|
@ -336,12 +350,12 @@ export abstract class AbstractSideBySideEditorInputSerializer implements IEditor
|
|||
return [registry.getEditorSerializer(secondaryEditorInputTypeId), registry.getEditorSerializer(primaryEditorInputTypeId)];
|
||||
}
|
||||
|
||||
protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
|
||||
protected abstract createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
|
||||
}
|
||||
|
||||
export class SideBySideEditorInputSerializer extends AbstractSideBySideEditorInputSerializer {
|
||||
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
protected createEditorInput(instantiationService: IInstantiationService, name: string | undefined, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
|
||||
return instantiationService.createInstance(SideBySideEditorInput, name, description, secondaryInput, primaryInput);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,23 +249,19 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant {
|
|||
const editorOrModel = findEditor(textEditorModel, this.codeEditorService) || textEditorModel;
|
||||
const mode = this.configurationService.getValue<'file' | 'modifications' | 'modificationsIfAvailable'>('editor.formatOnSaveMode', overrides);
|
||||
|
||||
// keeping things DRY :)
|
||||
const formatWholeFile = async () => {
|
||||
if (mode === 'file') {
|
||||
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token);
|
||||
};
|
||||
|
||||
if (mode === 'modifications' || mode === 'modificationsIfAvailable') {
|
||||
// try formatting modifications
|
||||
const ranges = await this.instantiationService.invokeFunction(getModifiedRanges, isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel);
|
||||
if (ranges) {
|
||||
// version control reports changes
|
||||
await this.instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, editorOrModel, ranges, FormattingMode.Silent, nestedProgress, token);
|
||||
} else if (ranges === null) {
|
||||
// version control not found
|
||||
await formatWholeFile();
|
||||
}
|
||||
} else {
|
||||
await formatWholeFile();
|
||||
const ranges = await this.instantiationService.invokeFunction(getModifiedRanges, isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel);
|
||||
if (ranges === null && mode === 'modificationsIfAvailable') {
|
||||
// no SCM, fallback to formatting the whole file iff wanted
|
||||
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token);
|
||||
|
||||
} else if (ranges) {
|
||||
// formatted modified ranges
|
||||
await this.instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, editorOrModel, ranges, FormattingMode.Silent, nestedProgress, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -575,10 +575,6 @@ class Launch extends AbstractLaunch implements ILaunch {
|
|||
}
|
||||
}
|
||||
|
||||
if (content === '') {
|
||||
return { editor: null, created: false };
|
||||
}
|
||||
|
||||
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
|
||||
let startLineNumber = 1;
|
||||
for (let i = 0; i < index; i++) {
|
||||
|
|
|
@ -3,66 +3,67 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/repl';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { dispose, IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { IDebugService, DEBUG_SCHEME, CONTEXT_IN_DEBUG_REPL, IDebugSession, State, IReplElement, IDebugConfiguration, REPL_VIEW_ID, CONTEXT_MULTI_SESSION_REPL, CONTEXT_DEBUG_STATE, getStateLabel } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { HistoryNavigator } from 'vs/base/common/history';
|
||||
import { IHistoryNavigationWidget } from 'vs/base/browser/history';
|
||||
import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { getSimpleEditorOptions, getSimpleCodeEditorWidgetOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
|
||||
import { CompletionContext, CompletionList, CompletionProviderRegistry, CompletionItem, completionKindFromString, CompletionItemKind, CompletionItemInsertTextRule } from 'vs/editor/common/modes';
|
||||
import { ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ReplDelegate, ReplVariablesRenderer, ReplSimpleElementsRenderer, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplRawObjectsRenderer, ReplDataSource, ReplAccessibilityProvider, ReplGroupRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
|
||||
import { ReplFilter, ReplFilterState, ReplFilterActionViewItem } from 'vs/workbench/contrib/debug/browser/replFilter';
|
||||
import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons';
|
||||
import { registerAction2, MenuId, Action2, IMenuService, IMenu } from 'vs/platform/actions/common/actions';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import * as aria from 'vs/base/browser/ui/aria/aria';
|
||||
import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor';
|
||||
import { IAsyncDataSource, ITreeContextMenuEvent, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { HistoryNavigator } from 'vs/base/common/history';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
|
||||
import { URI as uri } from 'vs/base/common/uri';
|
||||
import 'vs/css!./media/repl';
|
||||
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget';
|
||||
import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IDecorationOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CompletionContext, CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, completionKindFromString, CompletionList, CompletionProviderRegistry } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
||||
import { localize } from 'vs/nls';
|
||||
import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
|
||||
import { createAndBindHistoryNavigationWidgetScopedContextKeyService } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IViewPaneOptions, ViewAction, ViewPane } from 'vs/workbench/browser/parts/views/viewPane';
|
||||
import { IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
|
||||
import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions';
|
||||
import { FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
|
||||
import { debugConsoleClearAll, debugConsoleEvaluationPrompt } from 'vs/workbench/contrib/debug/browser/debugIcons';
|
||||
import { LinkDetector } from 'vs/workbench/contrib/debug/browser/linkDetector';
|
||||
import { ReplFilter, ReplFilterActionViewItem, ReplFilterState } from 'vs/workbench/contrib/debug/browser/replFilter';
|
||||
import { ReplAccessibilityProvider, ReplDataSource, ReplDelegate, ReplEvaluationInputsRenderer, ReplEvaluationResultsRenderer, ReplGroupRenderer, ReplRawObjectsRenderer, ReplSimpleElementsRenderer, ReplVariablesRenderer } from 'vs/workbench/contrib/debug/browser/replViewer';
|
||||
import { CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_REPL, CONTEXT_MULTI_SESSION_REPL, DEBUG_SCHEME, getStateLabel, IDebugConfiguration, IDebugService, IDebugSession, IReplElement, REPL_VIEW_ID, State } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
|
@ -493,7 +494,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget {
|
|||
} else if (action.id === FILTER_ACTION_ID) {
|
||||
const filterHistory = JSON.parse(this.storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[];
|
||||
this.filterActionViewItem = this.instantiationService.createInstance(ReplFilterActionViewItem, action,
|
||||
localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory);
|
||||
localize({ key: 'workbench.debug.filter.placeholder', comment: ['Text in the brackets after e.g. is not localizable'] }, "Filter (e.g. text, !exclude)"), this.filterState, filterHistory, () => showHistoryKeybindingHint(this.keybindingService));
|
||||
return this.filterActionViewItem;
|
||||
}
|
||||
|
||||
|
|
|
@ -138,6 +138,7 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
|
|||
private placeholder: string,
|
||||
private filters: ReplFilterState,
|
||||
private history: string[],
|
||||
private showHistoryHint: () => boolean,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService) {
|
||||
|
@ -188,7 +189,8 @@ export class ReplFilterActionViewItem extends BaseActionViewItem {
|
|||
private createInput(container: HTMLElement): void {
|
||||
this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, {
|
||||
placeholder: this.placeholder,
|
||||
history: this.history
|
||||
history: this.history,
|
||||
showHistoryHint: this.showHistoryHint
|
||||
}));
|
||||
this._register(attachInputBoxStyler(this.filterInputBox, this.themeService));
|
||||
this.filterInputBox.value = this.filters.filterText;
|
||||
|
|
|
@ -224,8 +224,10 @@ export class ExtensionEditor extends EditorPane {
|
|||
|
||||
const subtitle = append(details, $('.subtitle'));
|
||||
const publisher = append(append(subtitle, $('.subtitle-entry')), $('span.publisher.clickable', { title: localize('publisher', "Publisher name"), tabIndex: 0 }));
|
||||
publisher.setAttribute('role', 'button');
|
||||
const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { title: localize('install count', "Install count"), tabIndex: 0 }));
|
||||
const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { title: localize('rating', "Rating"), tabIndex: 0 }));
|
||||
rating.setAttribute('role', 'link'); // #132645
|
||||
|
||||
const description = append(details, $('.description'));
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ class EditorStatusContribution implements IWorkbenchContribution {
|
|||
|
||||
} else {
|
||||
const [first] = model.combined;
|
||||
let text: string = '$(info)';
|
||||
let text: string = '$(check-all)';
|
||||
if (first.severity === Severity.Error) {
|
||||
text = '$(error)';
|
||||
} else if (first.severity === Severity.Warning) {
|
||||
|
@ -279,7 +279,7 @@ class EditorStatusContribution implements IWorkbenchContribution {
|
|||
|
||||
private static _asStatusbarEntry(item: ILanguageStatus): IStatusbarEntry {
|
||||
return {
|
||||
name: item.name,
|
||||
name: localize('name.pattern', '{0} (Language Status)', item.name),
|
||||
text: item.label,
|
||||
ariaLabel: item.accessibilityInfo?.label ?? item.label,
|
||||
role: item.accessibilityInfo?.role,
|
||||
|
|
|
@ -29,6 +29,8 @@ import { BaseActionViewItem, ActionViewItem } from 'vs/base/browser/ui/actionbar
|
|||
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
|
||||
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
|
||||
import { IMarkersView } from 'vs/workbench/contrib/markers/browser/markers';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
|
||||
export interface IMarkersFiltersChangeEvent {
|
||||
filterText?: boolean;
|
||||
|
@ -245,6 +247,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
|
|||
private focusContextKey: IContextKey<boolean>;
|
||||
private readonly filtersAction: IAction;
|
||||
private actionbar: ActionBar | null = null;
|
||||
private keybindingService;
|
||||
|
||||
constructor(
|
||||
action: IAction,
|
||||
|
@ -252,9 +255,11 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
|
|||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(null, action);
|
||||
this.keybindingService = keybindingService;
|
||||
this.focusContextKey = Constants.MarkerViewFilterFocusContextKey.bindTo(contextKeyService);
|
||||
this.delayedFilterUpdate = new Delayer<void>(400);
|
||||
this._register(toDisposable(() => this.delayedFilterUpdate.cancel()));
|
||||
|
@ -319,7 +324,8 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem {
|
|||
this.filterInputBox = this._register(this.instantiationService.createInstance(ContextScopedHistoryInputBox, container, this.contextViewService, {
|
||||
placeholder: Messages.MARKERS_PANEL_FILTER_PLACEHOLDER,
|
||||
ariaLabel: Messages.MARKERS_PANEL_FILTER_ARIA_LABEL,
|
||||
history: this.markersView.filters.filterHistory
|
||||
history: this.markersView.filters.filterHistory,
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService)
|
||||
}));
|
||||
this._register(attachInputBoxStyler(this.filterInputBox, this.themeService));
|
||||
this.filterInputBox.value = this.markersView.filters.filterText;
|
||||
|
|
|
@ -160,6 +160,7 @@ class NotebookOutlineRenderer implements ITreeRenderer<OutlineEntry, FuzzyScore,
|
|||
renderElement(node: ITreeNode<OutlineEntry, FuzzyScore>, _index: number, template: NotebookOutlineTemplate, _height: number | undefined): void {
|
||||
const options: IIconLabelValueOptions = {
|
||||
matches: createMatches(node.filterData),
|
||||
labelEscapeNewLines: true,
|
||||
extraClasses: []
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
|||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { getNotebookEditorFromEditorPane, ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions';
|
||||
import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
export class TroubleshootController extends Disposable implements INotebookEditorContribution {
|
||||
|
@ -41,7 +42,7 @@ export class TroubleshootController extends Disposable implements INotebookEdito
|
|||
|
||||
private _log(cell: ICellViewModel, e: any) {
|
||||
if (this._logging) {
|
||||
const oldHeight = this._notebookEditor.getViewHeight(cell);
|
||||
const oldHeight = (this._notebookEditor as NotebookEditorWidget).getViewHeight(cell);
|
||||
console.log(`cell#${cell.handle}`, e, `${oldHeight} -> ${cell.layoutInfo.totalHeight}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -648,3 +648,28 @@ export function insertCellAtIndex(viewModel: NotebookViewModel, index: number, s
|
|||
return viewModel.cellAt(index)!;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
* @param newIdx in an index scheme for the state of the tree after the current cell has been "removed"
|
||||
* @param synchronous
|
||||
* @param pushedToUndoStack
|
||||
*/
|
||||
export function moveCellToIdx(editor: IActiveNotebookEditor, index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean {
|
||||
const viewCell = editor.cellAt(index) as CellViewModel | undefined;
|
||||
if (!viewCell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
editor.textModel.applyEdits([
|
||||
{
|
||||
editType: CellEditType.Move,
|
||||
index,
|
||||
length,
|
||||
newIdx
|
||||
}
|
||||
], synchronous, { kind: SelectionStateType.Index, focus: editor.getFocus(), selections: editor.getSelections() }, () => ({ kind: SelectionStateType.Index, focus: { start: newIdx, end: newIdx + 1 }, selections: [{ start: newIdx, end: newIdx + 1 }] }), undefined);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -566,23 +566,12 @@ export interface INotebookEditor {
|
|||
*/
|
||||
revealRangeInCenterIfOutsideViewportAsync(cell: ICellViewModel, range: Range): Promise<void>;
|
||||
|
||||
/**
|
||||
* Get the view height of a cell (from the list view)
|
||||
*/
|
||||
getViewHeight(cell: ICellViewModel): number;
|
||||
|
||||
/**
|
||||
* @param startIndex Inclusive
|
||||
* @param endIndex Exclusive
|
||||
*/
|
||||
getCellRangeFromViewRange(startIndex: number, endIndex: number): ICellRange | undefined;
|
||||
|
||||
/**
|
||||
* @param startIndex Inclusive
|
||||
* @param endIndex Exclusive
|
||||
*/
|
||||
getCellsFromViewRange(startIndex: number, endIndex: number): ReadonlyArray<ICellViewModel>;
|
||||
|
||||
/**
|
||||
* Set hidden areas on cell text models.
|
||||
*/
|
||||
|
|
|
@ -9,10 +9,8 @@ import { EditorModel } from 'vs/workbench/common/editor/editorModel';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { INotebookDiffEditorModel, IResolvedNotebookEditorModel } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
class NotebookDiffEditorModel extends EditorModel implements INotebookDiffEditorModel {
|
||||
|
@ -52,8 +50,6 @@ export class NotebookDiffEditorInput extends DiffEditorInput {
|
|||
override readonly original: NotebookEditorInput,
|
||||
override readonly modified: NotebookEditorInput,
|
||||
public readonly viewType: string,
|
||||
@IFileService fileService: IFileService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IEditorService editorService: IEditorService
|
||||
) {
|
||||
super(
|
||||
|
@ -62,8 +58,6 @@ export class NotebookDiffEditorInput extends DiffEditorInput {
|
|||
original,
|
||||
modified,
|
||||
undefined,
|
||||
labelService,
|
||||
fileService,
|
||||
editorService
|
||||
);
|
||||
}
|
||||
|
|
|
@ -179,11 +179,11 @@ export class ListViewInfoAccessor extends Disposable {
|
|||
return [];
|
||||
}
|
||||
|
||||
return this.list.viewModel.getCells(range);
|
||||
return this.list.viewModel.getCellsInRange(range);
|
||||
}
|
||||
|
||||
getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {
|
||||
return this.list.viewModel?.getCells(range) ?? [];
|
||||
return this.list.viewModel?.getCellsInRange(range) ?? [];
|
||||
}
|
||||
|
||||
setCellEditorSelection(cell: ICellViewModel, range: Range): void {
|
||||
|
@ -1229,9 +1229,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
this._outputFocus.set(false);
|
||||
this.updateEditorFocus();
|
||||
|
||||
if (!this._overlayContainer.contains(document.activeElement)) {
|
||||
this._webviewFocused = false;
|
||||
}
|
||||
this._webviewFocused = false;
|
||||
}));
|
||||
|
||||
this._localStore.add(this._webview.webview.onDidFocus(() => {
|
||||
|
@ -1829,10 +1827,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
return this._listViewInfoAccessor.getCellRangeFromViewRange(startIndex, endIndex);
|
||||
}
|
||||
|
||||
getCellsFromViewRange(startIndex: number, endIndex: number): ReadonlyArray<ICellViewModel> {
|
||||
return this._listViewInfoAccessor.getCellsFromViewRange(startIndex, endIndex);
|
||||
}
|
||||
|
||||
getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {
|
||||
return this._listViewInfoAccessor.getCellsInRange(range);
|
||||
}
|
||||
|
@ -1899,7 +1893,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD
|
|||
}
|
||||
|
||||
const existingDecorations = this._decortionKeyToIds.get(key) || [];
|
||||
const newDecorations = this.viewModel.getCells(range).map(cell => ({
|
||||
const newDecorations = this.viewModel.getCellsInRange(range).map(cell => ({
|
||||
handle: cell.handle,
|
||||
options: { className: decorationRule.className, outputClassName: decorationRule.className, topClassName: decorationRule.topClassName }
|
||||
}));
|
||||
|
@ -2911,6 +2905,6 @@ registerThemingParticipant((theme, collector) => {
|
|||
|
||||
const iconForegroundColor = theme.getColor(iconForeground);
|
||||
if (iconForegroundColor) {
|
||||
collector.addRule(`.monaco-workbench .notebookOverlay .codicon-debug-continue { color: ${iconForegroundColor}; }`);
|
||||
collector.addRule(`.monaco-workbench .notebookOverlay .codicon-debug-continue { color: ${iconForegroundColor} !important; }`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -327,7 +327,7 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
|
|||
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
|
||||
const cell = this.element(i);
|
||||
if (cell.cellKind === CellKind.Code) {
|
||||
if (this._viewModel!.hasCell(cell.handle)) {
|
||||
if (this._viewModel!.hasCell(cell)) {
|
||||
hiddenOutputs.push(...cell?.outputsViewModels);
|
||||
} else {
|
||||
deletedOutputs.push(...cell?.outputsViewModels);
|
||||
|
@ -447,9 +447,9 @@ export class NotebookCellList extends WorkbenchList<CellViewModel> implements ID
|
|||
}
|
||||
|
||||
const selectionsLeft = [];
|
||||
this.getSelectedElements().map(el => el.handle).forEach(handle => {
|
||||
if (this._viewModel!.hasCell(handle)) {
|
||||
selectionsLeft.push(handle);
|
||||
this.getSelectedElements().forEach(el => {
|
||||
if (this._viewModel!.hasCell(el)) {
|
||||
selectionsLeft.push(el.handle);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -11,10 +11,9 @@ import { clamp } from 'vs/base/common/numbers';
|
|||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IBulkEditService, ResourceEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLinePreference, IModelDecorationOptions, IModelDeltaDecoration, IReadonlyTextBuffer, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { MultiModelEditStackElement, SingleModelEditStackElement } from 'vs/editor/common/model/editStack';
|
||||
import { IntervalNode, IntervalTree } from 'vs/editor/common/model/intervalTree';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
|
@ -31,7 +30,7 @@ import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewM
|
|||
import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { CellEditType, CellKind, ICell, INotebookSearchOptions, IOutputDto, ISelectionState, NotebookCellMetadata, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellKind, ICell, INotebookSearchOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceRanges } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
|
||||
export interface INotebookEditorViewState {
|
||||
|
@ -524,7 +523,7 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
|
|||
return this._viewCells[index];
|
||||
}
|
||||
|
||||
getCells(range?: ICellRange): ReadonlyArray<ICellViewModel> {
|
||||
getCellsInRange(range?: ICellRange): ReadonlyArray<ICellViewModel> {
|
||||
if (!range) {
|
||||
return this._viewCells.slice(0);
|
||||
}
|
||||
|
@ -589,8 +588,8 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
|
|||
return index + 1;
|
||||
}
|
||||
|
||||
hasCell(handle: number) {
|
||||
return this._handleToViewCellMapping.has(handle);
|
||||
hasCell(cell: ICellViewModel) {
|
||||
return this._handleToViewCellMapping.has(cell.handle);
|
||||
}
|
||||
|
||||
getVersionId() {
|
||||
|
@ -774,142 +773,6 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD
|
|||
}
|
||||
}
|
||||
|
||||
createCell(index: number, source: string, language: string, type: CellKind, metadata: NotebookCellMetadata | undefined, outputs: IOutputDto[], synchronous: boolean, pushUndoStop: boolean = true, previouslyPrimary: number | null = null, previouslyFocused: ICellViewModel[] = []): CellViewModel {
|
||||
const beforeSelections = previouslyFocused.map(e => e.handle);
|
||||
const endSelections: ISelectionState = { kind: SelectionStateType.Index, focus: { start: index, end: index + 1 }, selections: [{ start: index, end: index + 1 }] };
|
||||
this._notebook.applyEdits([
|
||||
{
|
||||
editType: CellEditType.Replace,
|
||||
index,
|
||||
count: 0,
|
||||
cells: [
|
||||
{
|
||||
cellKind: type,
|
||||
language: language,
|
||||
mime: undefined,
|
||||
outputs: outputs,
|
||||
metadata: metadata,
|
||||
source: source
|
||||
}
|
||||
]
|
||||
}
|
||||
], synchronous, { kind: SelectionStateType.Handle, primary: previouslyPrimary, selections: beforeSelections }, () => endSelections, undefined);
|
||||
return this._viewCells[index];
|
||||
}
|
||||
|
||||
deleteCell(index: number, synchronous: boolean, pushUndoStop: boolean = true) {
|
||||
const focusSelectionIndex = this.getFocus()?.start ?? null;
|
||||
let endPrimarySelection: number | null = null;
|
||||
|
||||
if (index === focusSelectionIndex) {
|
||||
if (focusSelectionIndex < this.length - 1) {
|
||||
endPrimarySelection = this._viewCells[focusSelectionIndex + 1].handle;
|
||||
} else if (focusSelectionIndex === this.length - 1 && this.length > 1) {
|
||||
endPrimarySelection = this._viewCells[focusSelectionIndex - 1].handle;
|
||||
}
|
||||
}
|
||||
|
||||
let endSelections: number[] = this.selectionHandles.filter(handle => handle !== endPrimarySelection && handle !== this._viewCells[index]?.handle);
|
||||
|
||||
this._notebook.applyEdits([
|
||||
{
|
||||
editType: CellEditType.Replace,
|
||||
index: index,
|
||||
count: 1,
|
||||
cells: []
|
||||
}],
|
||||
synchronous,
|
||||
{ kind: SelectionStateType.Index, focus: this.getFocus(), selections: this.getSelections() },
|
||||
() => ({ kind: SelectionStateType.Handle, primary: endPrimarySelection, selections: endSelections }),
|
||||
undefined,
|
||||
pushUndoStop
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param index
|
||||
* @param length
|
||||
* @param newIdx in an index scheme for the state of the tree after the current cell has been "removed"
|
||||
* @param synchronous
|
||||
* @param pushedToUndoStack
|
||||
*/
|
||||
moveCellToIdx(index: number, length: number, newIdx: number, synchronous: boolean, pushedToUndoStack: boolean = true): boolean {
|
||||
const viewCell = this.viewCells[index] as CellViewModel;
|
||||
if (!viewCell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._notebook.applyEdits([
|
||||
{
|
||||
editType: CellEditType.Move,
|
||||
index,
|
||||
length,
|
||||
newIdx
|
||||
}
|
||||
], synchronous, { kind: SelectionStateType.Index, focus: this.getFocus(), selections: this.getSelections() }, () => ({ kind: SelectionStateType.Index, focus: { start: newIdx, end: newIdx + 1 }, selections: [{ start: newIdx, end: newIdx + 1 }] }), undefined);
|
||||
return true;
|
||||
}
|
||||
|
||||
private _pushIfAbsent(positions: IPosition[], p: IPosition) {
|
||||
const last = positions.length > 0 ? positions[positions.length - 1] : undefined;
|
||||
if (!last || last.lineNumber !== p.lineNumber || last.column !== p.column) {
|
||||
positions.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add split point at the beginning and the end;
|
||||
* Move end of line split points to the beginning of the next line;
|
||||
* Avoid duplicate split points
|
||||
*/
|
||||
private _splitPointsToBoundaries(splitPoints: IPosition[], textBuffer: IReadonlyTextBuffer): IPosition[] | null {
|
||||
const boundaries: IPosition[] = [];
|
||||
const lineCnt = textBuffer.getLineCount();
|
||||
const getLineLen = (lineNumber: number) => {
|
||||
return textBuffer.getLineLength(lineNumber);
|
||||
};
|
||||
|
||||
// split points need to be sorted
|
||||
splitPoints = splitPoints.sort((l, r) => {
|
||||
const lineDiff = l.lineNumber - r.lineNumber;
|
||||
const columnDiff = l.column - r.column;
|
||||
return lineDiff !== 0 ? lineDiff : columnDiff;
|
||||
});
|
||||
|
||||
for (let sp of splitPoints) {
|
||||
if (getLineLen(sp.lineNumber) + 1 === sp.column && sp.column !== 1 /** empty line */ && sp.lineNumber < lineCnt) {
|
||||
sp = new Position(sp.lineNumber + 1, 1);
|
||||
}
|
||||
this._pushIfAbsent(boundaries, sp);
|
||||
}
|
||||
|
||||
if (boundaries.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// boundaries already sorted and not empty
|
||||
const modelStart = new Position(1, 1);
|
||||
const modelEnd = new Position(lineCnt, getLineLen(lineCnt) + 1);
|
||||
return [modelStart, ...boundaries, modelEnd];
|
||||
}
|
||||
|
||||
computeCellLinesContents(cell: ICellViewModel, splitPoints: IPosition[]): string[] | null {
|
||||
const rangeBoundaries = this._splitPointsToBoundaries(splitPoints, cell.textBuffer);
|
||||
if (!rangeBoundaries) {
|
||||
return null;
|
||||
}
|
||||
const newLineModels: string[] = [];
|
||||
for (let i = 1; i < rangeBoundaries.length; i++) {
|
||||
const start = rangeBoundaries[i - 1];
|
||||
const end = rangeBoundaries[i];
|
||||
|
||||
newLineModels.push(cell.textBuffer.getValueInRange(new Range(start.lineNumber, start.column, end.lineNumber, end.column), EndOfLinePreference.TextDefined));
|
||||
}
|
||||
|
||||
return newLineModels;
|
||||
}
|
||||
|
||||
getEditorViewState(): INotebookEditorViewState {
|
||||
const editingCells: { [key: number]: boolean; } = {};
|
||||
this._viewCells.forEach((cell, i) => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { FoldingModel, updateFoldingStateAtIndex } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
|
||||
import { changeCellToKind, computeCellLinesContents, copyCellRange, joinNotebookCells, moveCellRange, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
import { changeCellToKind, computeCellLinesContents, copyCellRange, joinNotebookCells, moveCellRange, moveCellToIdx, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
import { CellEditType, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
@ -13,6 +13,56 @@ import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService';
|
|||
import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits';
|
||||
|
||||
suite('CellOperations', () => {
|
||||
test('move cells down', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['//a', 'javascript', CellKind.Code, [], {}],
|
||||
['//b', 'javascript', CellKind.Code, [], {}],
|
||||
['//c', 'javascript', CellKind.Code, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
moveCellToIdx(editor, 0, 1, 0, true);
|
||||
// no-op
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//b');
|
||||
|
||||
moveCellToIdx(editor, 0, 1, 1, true);
|
||||
// b, a, c
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//c');
|
||||
|
||||
moveCellToIdx(editor, 0, 1, 2, true);
|
||||
// a, c, b
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//c');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//b');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('move cells up', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['//a', 'javascript', CellKind.Code, [], {}],
|
||||
['//b', 'javascript', CellKind.Code, [], {}],
|
||||
['//c', 'javascript', CellKind.Code, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
moveCellToIdx(editor, 1, 1, 0, true);
|
||||
// b, a, c
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//a');
|
||||
|
||||
moveCellToIdx(editor, 2, 1, 0, true);
|
||||
// c, b, a
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//c');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//a');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Move cells - single cell', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
|
|
|
@ -52,8 +52,8 @@ suite('ListViewInfoAccessor', () => {
|
|||
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellRangeFromViewRange(0, 1), { start: 0, end: 2 });
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellRangeFromViewRange(1, 2), { start: 2, end: 5 });
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellsFromViewRange(0, 1), viewModel.getCells({ start: 0, end: 2 }));
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellsFromViewRange(1, 2), viewModel.getCells({ start: 2, end: 5 }));
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellsFromViewRange(0, 1), viewModel.getCellsInRange({ start: 0, end: 2 }));
|
||||
assert.deepStrictEqual(listViewInfoAccessor.getCellsFromViewRange(1, 2), viewModel.getCellsInRange({ start: 2, end: 5 }));
|
||||
|
||||
const notebookEditor = new class extends mock<INotebookEditor>() {
|
||||
override getViewIndexByModelIndex(index: number) { return listViewInfoAccessor.getViewIndex(viewModel.viewCells[index]!); }
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import * as assert from 'assert';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { FoldingModel, updateFoldingStateAtIndex } from 'vs/workbench/contrib/notebook/browser/contrib/fold/foldingModel';
|
||||
import { runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection';
|
||||
import { CellEditType, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { createNotebookCellList, setupInstantiationService, TestCell, withTestNotebook } from 'vs/workbench/contrib/notebook/test/testNotebookEditor';
|
||||
|
@ -288,9 +289,10 @@ suite('NotebookCellList focus/selection', () => {
|
|||
],
|
||||
(editor, viewModel) => {
|
||||
viewModel.updateSelectionsState({ kind: SelectionStateType.Index, focus: { start: 1, end: 2 }, selections: [{ start: 1, end: 2 }] });
|
||||
viewModel.deleteCell(1, true, false);
|
||||
runDeleteAction(editor, viewModel.cellAt(1)!);
|
||||
// viewModel.deleteCell(1, true, false);
|
||||
assert.deepStrictEqual(viewModel.getFocus(), { start: 0, end: 1 });
|
||||
assert.deepStrictEqual(viewModel.getSelections(), []);
|
||||
assert.deepStrictEqual(viewModel.getSelections(), [{ start: 0, end: 1 }]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
|
|||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { insertCellAtIndex } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
import { insertCellAtIndex, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations';
|
||||
import { reduceCellRanges } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookEventDispatcher } from 'vs/workbench/contrib/notebook/browser/viewModel/eventDispatcher';
|
||||
import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModel';
|
||||
|
@ -57,7 +57,7 @@ suite('NotebookViewModel', () => {
|
|||
assert.strictEqual(viewModel.notebookDocument.cells.length, 3);
|
||||
assert.strictEqual(viewModel.getCellIndex(cell), 1);
|
||||
|
||||
viewModel.deleteCell(1, true);
|
||||
runDeleteAction(editor, viewModel.cellAt(1)!);
|
||||
assert.strictEqual(viewModel.length, 2);
|
||||
assert.strictEqual(viewModel.notebookDocument.cells.length, 2);
|
||||
assert.strictEqual(viewModel.getCellIndex(cell), -1);
|
||||
|
@ -65,56 +65,6 @@ suite('NotebookViewModel', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('move cells down', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['//a', 'javascript', CellKind.Code, [], {}],
|
||||
['//b', 'javascript', CellKind.Code, [], {}],
|
||||
['//c', 'javascript', CellKind.Code, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
viewModel.moveCellToIdx(0, 1, 0, true);
|
||||
// no-op
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//b');
|
||||
|
||||
viewModel.moveCellToIdx(0, 1, 1, true);
|
||||
// b, a, c
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//c');
|
||||
|
||||
viewModel.moveCellToIdx(0, 1, 2, true);
|
||||
// a, c, b
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//a');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//c');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//b');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('move cells up', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
['//a', 'javascript', CellKind.Code, [], {}],
|
||||
['//b', 'javascript', CellKind.Code, [], {}],
|
||||
['//c', 'javascript', CellKind.Code, [], {}],
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
viewModel.moveCellToIdx(1, 1, 0, true);
|
||||
// b, a, c
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//a');
|
||||
|
||||
viewModel.moveCellToIdx(2, 1, 0, true);
|
||||
// c, b, a
|
||||
assert.strictEqual(viewModel.cellAt(0)?.getText(), '//c');
|
||||
assert.strictEqual(viewModel.cellAt(1)?.getText(), '//b');
|
||||
assert.strictEqual(viewModel.cellAt(2)?.getText(), '//a');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('index', async function () {
|
||||
await withTestNotebook(
|
||||
[
|
||||
|
@ -129,7 +79,7 @@ suite('NotebookViewModel', () => {
|
|||
const cell = insertCellAtIndex(viewModel, insertIndex, 'var c = 3;', 'javascript', CellKind.Code, {}, [], true);
|
||||
|
||||
const addedCellIndex = viewModel.getCellIndex(cell);
|
||||
viewModel.deleteCell(addedCellIndex, true);
|
||||
runDeleteAction(editor, viewModel.cellAt(addedCellIndex)!);
|
||||
|
||||
const secondInsertIndex = viewModel.getCellIndex(lastViewCell) + 1;
|
||||
const cell2 = insertCellAtIndex(viewModel, secondInsertIndex, 'var d = 4;', 'javascript', CellKind.Code, {}, [], true);
|
||||
|
@ -192,7 +142,7 @@ suite('NotebookViewModel Decorations', () => {
|
|||
end: 3
|
||||
});
|
||||
|
||||
viewModel.deleteCell(0, true);
|
||||
runDeleteAction(editor, viewModel.cellAt(0)!);
|
||||
assert.deepStrictEqual(viewModel.getTrackedRange(trackedId!), {
|
||||
start: 1,
|
||||
|
||||
|
@ -206,14 +156,14 @@ suite('NotebookViewModel Decorations', () => {
|
|||
end: 3
|
||||
});
|
||||
|
||||
viewModel.deleteCell(3, true);
|
||||
runDeleteAction(editor, viewModel.cellAt(3)!);
|
||||
assert.deepStrictEqual(viewModel.getTrackedRange(trackedId!), {
|
||||
start: 1,
|
||||
|
||||
end: 2
|
||||
});
|
||||
|
||||
viewModel.deleteCell(1, true);
|
||||
runDeleteAction(editor, viewModel.cellAt(1)!);
|
||||
assert.deepStrictEqual(viewModel.getTrackedRange(trackedId!), {
|
||||
start: 0,
|
||||
|
||||
|
@ -370,18 +320,18 @@ suite('NotebookViewModel API', () => {
|
|||
['# header b', 'markdown', CellKind.Markup, [], {}]
|
||||
],
|
||||
(editor, viewModel) => {
|
||||
assert.strictEqual(viewModel.getCells().length, 3);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 0, end: 1 }).map(cell => cell.getText()), ['# header a']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 0, end: 2 }).map(cell => cell.getText()), ['# header a', 'var b = 1;']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 0, end: 3 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 0, end: 4 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 1, end: 4 }).map(cell => cell.getText()), ['var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 2, end: 4 }).map(cell => cell.getText()), ['# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 3, end: 4 }).map(cell => cell.getText()), []);
|
||||
assert.strictEqual(viewModel.getCellsInRange().length, 3);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 0, end: 1 }).map(cell => cell.getText()), ['# header a']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 0, end: 2 }).map(cell => cell.getText()), ['# header a', 'var b = 1;']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 0, end: 3 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 0, end: 4 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 1, end: 4 }).map(cell => cell.getText()), ['var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 2, end: 4 }).map(cell => cell.getText()), ['# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 3, end: 4 }).map(cell => cell.getText()), []);
|
||||
|
||||
// no one should use an invalid range but `getCells` should be able to handle that.
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: -1, end: 1 }).map(cell => cell.getText()), ['# header a']);
|
||||
assert.deepStrictEqual(viewModel.getCells({ start: 3, end: 0 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: -1, end: 1 }).map(cell => cell.getText()), ['# header a']);
|
||||
assert.deepStrictEqual(viewModel.getCellsInRange({ start: 3, end: 0 }).map(cell => cell.getText()), ['# header a', 'var b = 1;', '# header b']);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -249,7 +249,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic
|
|||
override cellAt(index: number) { return viewModel.cellAt(index)!; }
|
||||
override getCellIndex(cell: ICellViewModel) { return viewModel.getCellIndex(cell); }
|
||||
override getCellIndexByHandle(handle: number) { return viewModel.getCellIndexByHandle(handle); }
|
||||
override getCellsInRange(range?: ICellRange) { return viewModel.getCells(range); }
|
||||
override getCellsInRange(range?: ICellRange) { return viewModel.getCellsInRange(range); }
|
||||
override getNextVisibleCellIndex(index: number) { return viewModel.getNextVisibleCellIndex(index); }
|
||||
getControl() { return this; }
|
||||
override get onDidChangeSelection() { return viewModel.onDidChangeSelection as Event<any>; }
|
||||
|
|
|
@ -54,12 +54,12 @@ export class KeybindingsSearchWidget extends SearchWidget {
|
|||
|
||||
constructor(parent: HTMLElement, options: KeybindingsSearchOptions,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
) {
|
||||
super(parent, options, contextViewService, instantiationService, themeService, contextKeyService);
|
||||
super(parent, options, contextViewService, instantiationService, themeService, contextKeyService, keybindingService);
|
||||
this._register(attachInputBoxStyler(this.inputBox, themeService));
|
||||
this._register(toDisposable(() => this.stopRecordingKeys()));
|
||||
this._firstPart = null;
|
||||
|
|
|
@ -25,7 +25,7 @@ import { DefineKeybindingWidget, KeybindingsSearchWidget } from 'vs/workbench/co
|
|||
import { CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE } from 'vs/workbench/contrib/preferences/common/preferences';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
|
||||
import { IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector, ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
|
@ -494,9 +494,10 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP
|
|||
)) as WorkbenchTable<IKeybindingItemEntry>;
|
||||
|
||||
this._register(this.keybindingsTable.onContextMenu(e => this.onContextMenu(e)));
|
||||
this._register(this.keybindingsTable.onDidChangeFocus(e => this.onFocusChange(e)));
|
||||
this._register(this.keybindingsTable.onDidChangeFocus(e => this.onFocusChange()));
|
||||
this._register(this.keybindingsTable.onDidFocus(() => {
|
||||
this.keybindingsTable.getHTMLElement().classList.add('focused');
|
||||
this.onFocusChange();
|
||||
}));
|
||||
this._register(this.keybindingsTable.onDidBlur(() => {
|
||||
this.keybindingsTable.getHTMLElement().classList.remove('focused');
|
||||
|
@ -685,9 +686,9 @@ export class KeybindingsEditor extends EditorPane implements IKeybindingsEditorP
|
|||
}
|
||||
}
|
||||
|
||||
private onFocusChange(e: IListEvent<IKeybindingItemEntry>): void {
|
||||
private onFocusChange(): void {
|
||||
this.keybindingFocusContextKey.reset();
|
||||
const element = e.elements[0];
|
||||
const element = this.keybindingsTable.getFocusedElements()[0];
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -22,10 +22,12 @@ import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/brows
|
|||
import { IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { activeContrastBorder, badgeBackground, badgeForeground, contrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachInputBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
|
||||
|
@ -370,7 +372,8 @@ export class SearchWidget extends Widget {
|
|||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IInstantiationService protected instantiationService: IInstantiationService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IKeybindingService protected readonly keybindingService: IKeybindingService
|
||||
) {
|
||||
super();
|
||||
this.create(parent);
|
||||
|
@ -420,7 +423,8 @@ export class SearchWidget extends Widget {
|
|||
}
|
||||
|
||||
protected createInputBox(parent: HTMLElement): HistoryInputBox {
|
||||
const box = this._register(new ContextScopedHistoryInputBox(parent, this.contextViewService, this.options, this.contextKeyService));
|
||||
const showHistoryHint = () => showHistoryKeybindingHint(this.keybindingService);
|
||||
const box = this._register(new ContextScopedHistoryInputBox(parent, this.contextViewService, { ...this.options, showHistoryHint }, this.contextKeyService));
|
||||
this._register(attachInputBoxStyler(box, this.themeService));
|
||||
|
||||
return box;
|
||||
|
|
|
@ -15,8 +15,10 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
|||
import type { IThemable } from 'vs/base/common/styler';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { attachCheckboxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
|
@ -49,7 +51,8 @@ export class PatternInputWidget extends Widget implements IThemable {
|
|||
constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
|
||||
@IThemeService protected themeService: IThemeService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
) {
|
||||
super();
|
||||
options = {
|
||||
|
@ -151,7 +154,8 @@ export class PatternInputWidget extends Widget implements IThemable {
|
|||
validationOptions: {
|
||||
validation: undefined
|
||||
},
|
||||
history: options.history || []
|
||||
history: options.history || [],
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService)
|
||||
}, this.contextKeyService);
|
||||
this._register(attachInputBoxStyler(this.inputBox, this.themeService));
|
||||
this._register(this.inputBox.onDidChange(() => this._onSubmit.fire(true)));
|
||||
|
@ -192,8 +196,9 @@ export class IncludePatternInputWidget extends PatternInputWidget {
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
) {
|
||||
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService);
|
||||
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService, keybindingService);
|
||||
}
|
||||
|
||||
private useSearchInEditorsBox!: Checkbox;
|
||||
|
@ -243,8 +248,9 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
) {
|
||||
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService);
|
||||
super(parent, contextViewProvider, options, themeService, contextKeyService, configurationService, keybindingService);
|
||||
}
|
||||
|
||||
private useExcludesAndIgnoreFilesBox!: Checkbox;
|
||||
|
|
|
@ -320,7 +320,7 @@ export class SearchView extends ViewPane {
|
|||
|
||||
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(IncludePatternInputWidget, folderIncludesList, this.contextViewService, {
|
||||
ariaLabel: filesToIncludeTitle,
|
||||
placeholder: nls.localize('placeholder.includes', "(e.g. *.ts, src/**/include)"),
|
||||
placeholder: nls.localize('placeholder.includes', "e.g. *.ts, src/**/include"),
|
||||
showPlaceholderOnFocus: true,
|
||||
history: patternIncludesHistory,
|
||||
}));
|
||||
|
@ -339,7 +339,7 @@ export class SearchView extends ViewPane {
|
|||
dom.append(excludesList, $('h4', undefined, excludesTitle));
|
||||
this.inputPatternExcludes = this._register(this.instantiationService.createInstance(ExcludePatternInputWidget, excludesList, this.contextViewService, {
|
||||
ariaLabel: excludesTitle,
|
||||
placeholder: nls.localize('placeholder.excludes', "(e.g. *.ts, src/**/exclude)"),
|
||||
placeholder: nls.localize('placeholder.excludes', "e.g. *.ts, src/**/exclude"),
|
||||
showPlaceholderOnFocus: true,
|
||||
history: patternExclusionsHistory,
|
||||
}));
|
||||
|
|
|
@ -35,6 +35,7 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
|
|||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
import { searchReplaceAllIcon, searchHideReplaceIcon, searchShowContextIcon, searchShowReplaceIcon } from 'vs/workbench/contrib/search/browser/searchIcons';
|
||||
import { ToggleSearchEditorContextLinesCommandId } from 'vs/workbench/contrib/searchEditor/browser/constants';
|
||||
import { showHistoryKeybindingHint } from 'vs/platform/browser/historyWidgetKeybindingHint';
|
||||
|
||||
/** Specified in searchview.css */
|
||||
export const SingleLineInputHeight = 24;
|
||||
|
@ -156,7 +157,7 @@ export class SearchWidget extends Widget {
|
|||
@IContextViewService private readonly contextViewService: IContextViewService,
|
||||
@IThemeService private readonly themeService: IThemeService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private readonly keyBindingService: IKeybindingService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IClipboardService private readonly clipboardServce: IClipboardService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
|
||||
|
@ -235,6 +236,7 @@ export class SearchWidget extends Widget {
|
|||
|
||||
clearHistory(): void {
|
||||
this.searchInput.inputBox.clearHistory();
|
||||
this.replaceInput.inputBox.clearHistory();
|
||||
}
|
||||
|
||||
showNextSearchTerm() {
|
||||
|
@ -306,10 +308,11 @@ export class SearchWidget extends Widget {
|
|||
label: nls.localize('label.Search', 'Search: Type Search Term and press Enter to search'),
|
||||
validation: (value: string) => this.validateSearchInput(value),
|
||||
placeholder: nls.localize('search.placeHolder', "Search"),
|
||||
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keyBindingService),
|
||||
appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keyBindingService),
|
||||
appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleRegexCommandId), this.keyBindingService),
|
||||
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keybindingService.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keybindingService),
|
||||
appendWholeWordsLabel: appendKeyBindingLabel('', this.keybindingService.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keybindingService),
|
||||
appendRegexLabel: appendKeyBindingLabel('', this.keybindingService.lookupKeybinding(Constants.ToggleRegexCommandId), this.keybindingService),
|
||||
history: options.searchHistory,
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService),
|
||||
flexibleHeight: true,
|
||||
flexibleMaxHeight: SearchWidget.INPUT_MAX_HEIGHT
|
||||
};
|
||||
|
@ -354,7 +357,7 @@ export class SearchWidget extends Widget {
|
|||
|
||||
this.showContextCheckbox = new Checkbox({
|
||||
isChecked: false,
|
||||
title: appendKeyBindingLabel(nls.localize('showContext', "Toggle Context Lines"), this.keyBindingService.lookupKeybinding(ToggleSearchEditorContextLinesCommandId), this.keyBindingService),
|
||||
title: appendKeyBindingLabel(nls.localize('showContext', "Toggle Context Lines"), this.keybindingService.lookupKeybinding(ToggleSearchEditorContextLinesCommandId), this.keybindingService),
|
||||
icon: searchShowContextIcon
|
||||
});
|
||||
this._register(this.showContextCheckbox.onChange(() => this.onContextLinesChanged()));
|
||||
|
@ -396,8 +399,9 @@ export class SearchWidget extends Widget {
|
|||
this.replaceInput = this._register(new ContextScopedReplaceInput(replaceBox, this.contextViewService, {
|
||||
label: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview'),
|
||||
placeholder: nls.localize('search.replace.placeHolder', "Replace"),
|
||||
appendPreserveCaseLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.TogglePreserveCaseId), this.keyBindingService),
|
||||
appendPreserveCaseLabel: appendKeyBindingLabel('', this.keybindingService.lookupKeybinding(Constants.TogglePreserveCaseId), this.keybindingService),
|
||||
history: options.replaceHistory,
|
||||
showHistoryHint: () => showHistoryKeybindingHint(this.keybindingService),
|
||||
flexibleHeight: true,
|
||||
flexibleMaxHeight: SearchWidget.INPUT_MAX_HEIGHT
|
||||
}, this.contextKeyService, true));
|
||||
|
@ -452,7 +456,7 @@ export class SearchWidget extends Widget {
|
|||
setReplaceAllActionState(enabled: boolean): void {
|
||||
if (this.replaceAllAction.enabled !== enabled) {
|
||||
this.replaceAllAction.enabled = enabled;
|
||||
this.replaceAllAction.label = enabled ? SearchWidget.REPLACE_ALL_ENABLED_LABEL(this.keyBindingService) : SearchWidget.REPLACE_ALL_DISABLED_LABEL;
|
||||
this.replaceAllAction.label = enabled ? SearchWidget.REPLACE_ALL_ENABLED_LABEL(this.keybindingService) : SearchWidget.REPLACE_ALL_DISABLED_LABEL;
|
||||
this.updateReplaceActiveState();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -572,6 +572,8 @@ export interface ITerminalInstance {
|
|||
readonly navigationMode: INavigationMode | undefined;
|
||||
|
||||
description: string | undefined;
|
||||
|
||||
userHome: string | undefined
|
||||
/**
|
||||
* Shows the environment information hover if the widget exists.
|
||||
*/
|
||||
|
|
|
@ -543,7 +543,7 @@ export function registerTerminalActions() {
|
|||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageDown,
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow },
|
||||
when: TerminalContextKeys.focus,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: TerminalContextKeys.processSupported
|
||||
|
@ -583,7 +583,7 @@ export function registerTerminalActions() {
|
|||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.End,
|
||||
linux: { primary: KeyMod.Shift | KeyCode.End },
|
||||
when: TerminalContextKeys.focus,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: TerminalContextKeys.processSupported
|
||||
|
@ -603,7 +603,7 @@ export function registerTerminalActions() {
|
|||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.PageUp,
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.UpArrow },
|
||||
when: TerminalContextKeys.focus,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: TerminalContextKeys.processSupported
|
||||
|
@ -643,7 +643,7 @@ export function registerTerminalActions() {
|
|||
keybinding: {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.Home,
|
||||
linux: { primary: KeyMod.Shift | KeyCode.Home },
|
||||
when: TerminalContextKeys.focus,
|
||||
when: ContextKeyExpr.and(TerminalContextKeys.focus, TerminalContextKeys.altBufferActive.negate()),
|
||||
weight: KeybindingWeight.WorkbenchContrib
|
||||
},
|
||||
precondition: TerminalContextKeys.processSupported
|
||||
|
|
|
@ -22,7 +22,7 @@ import { IShellLaunchConfig } from 'vs/platform/terminal/common/terminal';
|
|||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
|
||||
const MINIMUM_FONT_SIZE = 6;
|
||||
const MAXIMUM_FONT_SIZE = 25;
|
||||
const MAXIMUM_FONT_SIZE = 100;
|
||||
|
||||
/**
|
||||
* Encapsulates terminal configuration logic, the primary purpose of this file is so that platform
|
||||
|
|
|
@ -70,6 +70,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
|||
import { TerminalEditorInput } from 'vs/workbench/contrib/terminal/browser/terminalEditorInput';
|
||||
import { isSafari } from 'vs/base/browser/browser';
|
||||
import { ISeparator, template } from 'vs/base/common/labels';
|
||||
import { IPathService } from 'vs/workbench/services/path/common/pathService';
|
||||
|
||||
// How long in milliseconds should an average frame take to render for a notification to appear
|
||||
// which suggests the fallback DOM-based renderer
|
||||
|
@ -175,6 +176,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
private _staticTitle?: string;
|
||||
private _workspaceFolder?: string;
|
||||
private _labelComputer?: TerminalLabelComputer;
|
||||
private _userHome?: string;
|
||||
|
||||
target?: TerminalLocation;
|
||||
get instanceId(): number { return this._instanceId; }
|
||||
|
@ -231,6 +233,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
get initialCwd(): string | undefined { return this._initialCwd; }
|
||||
get capabilities(): ProcessCapability[] { return this._capabilities; }
|
||||
get description(): string | undefined { return this._description || this.shellLaunchConfig.description; }
|
||||
get userHome(): string | undefined { return this._userHome; }
|
||||
// The onExit event is special in that it fires and is disposed after the terminal instance
|
||||
// itself is disposed
|
||||
private readonly _onExit = new Emitter<number | undefined>();
|
||||
|
@ -278,6 +281,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
resource: URI | undefined,
|
||||
@ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService,
|
||||
@ITerminalProfileResolverService private readonly _terminalProfileResolverService: ITerminalProfileResolverService,
|
||||
@IPathService private readonly _pathService: IPathService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
|
||||
@IKeybindingService private readonly _keybindingService: IKeybindingService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
|
@ -610,6 +614,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
letterSpacing: font.letterSpacing,
|
||||
lineHeight: font.lineHeight,
|
||||
minimumContrastRatio: config.minimumContrastRatio,
|
||||
cursorBlink: config.cursorBlinking,
|
||||
cursorStyle: config.cursorStyle,
|
||||
cursorWidth: config.cursorWidth,
|
||||
bellStyle: 'none',
|
||||
macOptionIsMeta: config.macOptionIsMeta,
|
||||
macOptionClickForcesSelection: config.macOptionClickForcesSelection,
|
||||
|
@ -690,7 +697,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
|
||||
this._xtermTypeAhead = this._register(this._instantiationService.createInstance(TypeAheadAddon, this._processManager, this._configHelper));
|
||||
this._xterm.loadAddon(this._xtermTypeAhead);
|
||||
|
||||
this._userHome = (await this._pathService.userHome()).fsPath;
|
||||
return xterm;
|
||||
}
|
||||
|
||||
|
@ -1745,7 +1752,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
|
|||
|
||||
if (this._isVisible) {
|
||||
// HACK: Force the renderer to unpause by simulating an IntersectionObserver event.
|
||||
// This is to fix an issue where dragging the window to the top of the screen to
|
||||
// This is to fix an issue where dragging the windpow to the top of the screen to
|
||||
// maximize on Windows/Linux would fire an event saying that the terminal was not
|
||||
// visible.
|
||||
if (this._xterm.getOption('rendererType') === 'canvas') {
|
||||
|
@ -2281,7 +2288,7 @@ export class TerminalLabelComputer extends Disposable {
|
|||
readonly onDidChangeLabel = this._onDidChangeLabel.event;
|
||||
constructor(
|
||||
private readonly _configHelper: TerminalConfigHelper,
|
||||
private readonly _instance: Pick<ITerminalInstance, 'shellLaunchConfig' | 'cwd' | 'initialCwd' | 'processName' | 'sequence' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'>,
|
||||
private readonly _instance: Pick<ITerminalInstance, 'shellLaunchConfig' | 'cwd' | 'initialCwd' | 'processName' | 'sequence' | 'userHome' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'>,
|
||||
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService
|
||||
) {
|
||||
super();
|
||||
|
@ -2314,14 +2321,11 @@ export class TerminalLabelComputer extends Disposable {
|
|||
if (this._instance.staticTitle && labelType === TerminalLabelType.Title) {
|
||||
return this._instance.staticTitle.replace(/[\n\r\t]/g, '') || templateProperties.process?.replace(/[\n\r\t]/g, '') || '';
|
||||
}
|
||||
const detection = this._instance.capabilities.includes(ProcessCapability.CwdDetection);
|
||||
const zeroRootWorkspace = this._workspaceContextService.getWorkspace().folders.length === 0 && templateProperties.cwd === (this._instance.userHome || this._configHelper.config.cwd);
|
||||
const singleRootWorkspace = this._workspaceContextService.getWorkspace().folders.length === 1 && templateProperties.cwd === (this._configHelper.config.cwd || this._workspaceContextService.getWorkspace().folders[0]?.uri.fsPath);
|
||||
templateProperties.cwdFolder = (!templateProperties.cwd || !detection || zeroRootWorkspace || singleRootWorkspace) ? '' : path.basename(templateProperties.cwd);
|
||||
|
||||
if (!templateProperties.cwd || !this._instance.capabilities.includes(ProcessCapability.CwdDetection) ||
|
||||
(this._workspaceContextService.getWorkspace().folders.length <= 1 &&
|
||||
(templateProperties.cwd === (this._configHelper.config.cwd || (this._workspaceContextService.getWorkspace().folders[0].uri.fsPath))))) {
|
||||
templateProperties.cwdFolder = '';
|
||||
} else if (templateProperties.cwd && typeof templateProperties.cwd === 'string') {
|
||||
templateProperties.cwdFolder = path.basename(templateProperties.cwd);
|
||||
}
|
||||
//Remove special characters that could mess with rendering
|
||||
const label = template(labelTemplate, (templateProperties as unknown) as { [key: string]: string | ISeparator | undefined | null; }).replace(/[\n\r\t]/g, '');
|
||||
return label === '' && labelType === TerminalLabelType.Title ? (this._instance.processName || '') : label;
|
||||
|
|
|
@ -207,7 +207,7 @@ export class TerminalTabbedView extends Disposable {
|
|||
const style = window.getComputedStyle(this._tabListElement);
|
||||
ctx.font = `${style.fontStyle} ${style.fontSize} ${style.fontFamily}`;
|
||||
const maxInstanceWidth = this._terminalGroupService.instances.reduce((p, c) => {
|
||||
return Math.max(p, ctx.measureText(c.title + (c.shellLaunchConfig.description || '')).width + this._getAdditionalWidth(c));
|
||||
return Math.max(p, ctx.measureText(c.title + (c.description || '')).width + this._getAdditionalWidth(c));
|
||||
}, 0);
|
||||
idealWidth = Math.ceil(Math.max(maxInstanceWidth, TerminalTabsListSizes.WideViewMinimumWidth));
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ export class TerminalTabbedView extends Disposable {
|
|||
|
||||
private _getAdditionalWidth(instance: ITerminalInstance): number {
|
||||
// Size to include padding, icon, status icon (if any), split annotation (if any), + a little more
|
||||
const additionalWidth = 30;
|
||||
const additionalWidth = 40;
|
||||
const statusIconWidth = instance.statusList.statuses.length > 0 ? STATUS_ICON_WIDTH : 0;
|
||||
const splitAnnotationWidth = (this._terminalGroupService.getGroupForInstance(instance)?.terminalInstances.length || 0) > 1 ? SPLIT_ANNOTATION_WIDTH : 0;
|
||||
return additionalWidth + splitAnnotationWidth + statusIconWidth;
|
||||
|
|
|
@ -154,7 +154,7 @@ export interface ITerminalConfiguration {
|
|||
gpuAcceleration: 'auto' | 'on' | 'canvas' | 'off';
|
||||
rightClickBehavior: 'default' | 'copyPaste' | 'paste' | 'selectWord';
|
||||
cursorBlinking: boolean;
|
||||
cursorStyle: string;
|
||||
cursorStyle: 'block' | 'underline' | 'bar';
|
||||
cursorWidth: number;
|
||||
drawBoldTextInBrightColors: boolean;
|
||||
fastScrollSensitivity: number;
|
||||
|
@ -188,7 +188,6 @@ export interface ITerminalConfiguration {
|
|||
splitCwd: 'workspaceRoot' | 'initial' | 'inherited';
|
||||
windowsEnableConpty: boolean;
|
||||
wordSeparators: string;
|
||||
titleMode: 'executable' | 'sequence';
|
||||
enableFileLinks: boolean;
|
||||
unicodeVersion: '6' | '11';
|
||||
experimentalLinkProvider: boolean;
|
||||
|
|
|
@ -151,7 +151,9 @@ const terminalConfiguration: IConfigurationNode = {
|
|||
[TerminalSettingId.FontSize]: {
|
||||
description: localize('terminal.integrated.fontSize', "Controls the font size in pixels of the terminal."),
|
||||
type: 'number',
|
||||
default: isMacintosh ? 12 : 14
|
||||
default: isMacintosh ? 12 : 14,
|
||||
minimum: 6,
|
||||
maximum: 100
|
||||
},
|
||||
[TerminalSettingId.LetterSpacing]: {
|
||||
description: localize('terminal.integrated.letterSpacing', "Controls the letter spacing of the terminal, this is an integer value which represents the amount of additional pixels to add between characters."),
|
||||
|
@ -417,16 +419,6 @@ const terminalConfiguration: IConfigurationNode = {
|
|||
type: 'string',
|
||||
default: ' ()[]{}\',"`─'
|
||||
},
|
||||
[TerminalSettingId.TitleMode]: {
|
||||
description: localize('terminal.integrated.titleMode', "Determines how the terminal's title is set, this shows up in the terminal's tab or dropdown entry."),
|
||||
type: 'string',
|
||||
enum: ['executable', 'sequence'],
|
||||
markdownEnumDescriptions: [
|
||||
localize('titleMode.executable', "The title is set by the terminal, the name of the detected foreground process will be used."),
|
||||
localize('titleMode.sequence', "The title is set by the process via an escape sequence, this is useful if your shell dynamically sets the title.")
|
||||
],
|
||||
default: 'executable'
|
||||
},
|
||||
[TerminalSettingId.EnableFileLinks]: {
|
||||
description: localize('terminal.integrated.enableFileLinks', "Whether to enable file links in the terminal. Links can be slow when working on a network drive in particular because each file link is verified against the file system. Changing this will take effect only in new terminals."),
|
||||
type: 'boolean',
|
||||
|
|
|
@ -106,7 +106,7 @@ suite('Workbench - TerminalConfigHelper', () => {
|
|||
});
|
||||
configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!, null!);
|
||||
configHelper.panelContainer = fixture;
|
||||
assert.strictEqual(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it');
|
||||
assert.strictEqual(configHelper.getFont().fontSize, 100, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it');
|
||||
|
||||
await configurationService.setUserConfiguration('editor', {
|
||||
fontFamily: 'foo'
|
||||
|
|
|
@ -17,7 +17,7 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term
|
|||
import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { basename } from 'path';
|
||||
|
||||
function createInstance(partial?: Partial<ITerminalInstance>): Pick<ITerminalInstance, 'shellLaunchConfig' | 'cwd' | 'initialCwd' | 'processName' | 'sequence' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'> {
|
||||
function createInstance(partial?: Partial<ITerminalInstance>): Pick<ITerminalInstance, 'shellLaunchConfig' | 'userHome' | 'cwd' | 'initialCwd' | 'processName' | 'sequence' | 'workspaceFolder' | 'staticTitle' | 'capabilities' | 'title' | 'description'> {
|
||||
return {
|
||||
shellLaunchConfig: {},
|
||||
cwd: 'cwd',
|
||||
|
@ -29,6 +29,7 @@ function createInstance(partial?: Partial<ITerminalInstance>): Pick<ITerminalIns
|
|||
capabilities: isWindows ? [] : [ProcessCapability.CwdDetection],
|
||||
title: '',
|
||||
description: '',
|
||||
userHome: undefined,
|
||||
...partial
|
||||
};
|
||||
}
|
||||
|
@ -183,6 +184,7 @@ suite('Workbench - TerminalInstance', () => {
|
|||
strictEqual(terminalLabelComputer.description, 'root2');
|
||||
}
|
||||
});
|
||||
//TODO: enable and test userHome
|
||||
test.skip('should hide cwdFolder in empty workspaces when cwd matches the workspace\'s default cwd ($HOME or $HOMEDRIVE$HOMEPATH)', async () => {
|
||||
configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${cwdFolder}', description: '${cwdFolder}' } }, cwd: ROOT_1 } });
|
||||
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!);
|
||||
|
|
|
@ -89,7 +89,7 @@ export class TestingExplorerFilter extends BaseActionViewItem {
|
|||
value: this.state.text.value,
|
||||
placeholderText: localize('testExplorerFilter', "Filter (e.g. text, !exclude, @tag)"),
|
||||
},
|
||||
history: this.history.get([]),
|
||||
history: this.history.get([])
|
||||
}));
|
||||
this._register(attachSuggestEnabledInputBoxStyler(input, this.themeService));
|
||||
|
||||
|
|
|
@ -465,6 +465,10 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!this.configurationService.getValue('diffEditor.renderSideBySide')) {
|
||||
return isEqual(userDataSyncResource.merged, model.uri);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -142,7 +142,6 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
|
||||
// Fire event to outside parties
|
||||
this._onDidActiveEditorChange.fire();
|
||||
this._onDidEditorsChange.fire([{ groupId: activeGroup.id, editor: this.lastActiveEditor, kind: GroupChangeKind.EDITOR_ACTIVE }]);
|
||||
}
|
||||
|
||||
private registerGroupListeners(group: IEditorGroupView): void {
|
||||
|
@ -151,6 +150,9 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
groupDisposables.add(group.onDidGroupChange(e => {
|
||||
switch (e.kind) {
|
||||
case GroupChangeKind.EDITOR_ACTIVE:
|
||||
if (group.activeEditor) {
|
||||
this._onDidEditorsChange.fire([{ groupId: group.id, editor: group.activeEditor, kind: GroupChangeKind.EDITOR_ACTIVE }]);
|
||||
}
|
||||
this.handleActiveEditorChange(group);
|
||||
this._onDidVisibleEditorsChange.fire();
|
||||
break;
|
||||
|
|
|
@ -463,7 +463,7 @@ class CompositeMouseTracker extends Widget {
|
|||
private _isMouseIn: boolean = false;
|
||||
private _mouseTimeout: number | undefined;
|
||||
|
||||
private readonly _onMouseOut = new Emitter<void>();
|
||||
private readonly _onMouseOut = this._register(new Emitter<void>());
|
||||
get onMouseOut(): Event<void> { return this._onMouseOut.event; }
|
||||
|
||||
constructor(
|
||||
|
|
|
@ -103,7 +103,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
private registerListeners(): void {
|
||||
|
||||
// Creates
|
||||
this._register(this.files.onDidCreate(model => {
|
||||
this._register(this.files.onDidResolve(({ model }) => {
|
||||
if (model.isReadonly() || model.hasState(TextFileEditorModelState.ORPHAN)) {
|
||||
this._onDidChange.fire([model.resource]);
|
||||
}
|
||||
|
|
|
@ -947,9 +947,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
|||
case TextFileEditorModelState.ERROR:
|
||||
return this.inErrorMode;
|
||||
case TextFileEditorModelState.ORPHAN:
|
||||
if (this.resource.fsPath === '/Users/bpasero/Desktop/tab-labels/deleted.txt' || this.resource.fsPath === '/Users/bpasero/Desktop/tab-labels/deleted-readonly.txt') {
|
||||
return true;
|
||||
}
|
||||
return this.inOrphanMode;
|
||||
case TextFileEditorModelState.PENDING_SAVE:
|
||||
return this.saveSequentializer.hasPending();
|
||||
|
@ -1042,10 +1039,6 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
|
|||
}
|
||||
|
||||
override isReadonly(): boolean {
|
||||
if (this.resource.fsPath === '/Users/bpasero/Desktop/tab-labels/readonly.txt' || this.resource.fsPath === '/Users/bpasero/Desktop/tab-labels/deleted-readonly.txt') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.lastResolvedFileStat?.readonly || this.fileService.hasCapability(this.resource, FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue