mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-10 07:46:22 +01:00
Merge branch 'main' into do-162262
This commit is contained in:
@@ -94,41 +94,42 @@ impl ServerMessageSink {
|
||||
|
||||
async fn server_message_or_closed(
|
||||
&mut self,
|
||||
body: Option<&[u8]>,
|
||||
body_or_end: Option<&[u8]>,
|
||||
) -> Result<(), mpsc::error::SendError<SocketSignal>> {
|
||||
let i = self.id;
|
||||
let mut tx = self.tx.take().unwrap();
|
||||
let msg = body
|
||||
.map(|b| self.get_server_msg_content(b))
|
||||
.map(|body| RefServerMessageParams { i, body });
|
||||
|
||||
let r = match &mut tx {
|
||||
ServerMessageDestination::Channel(tx) => {
|
||||
tx.send(SocketSignal::from_message(&ToClientRequest {
|
||||
id: None,
|
||||
params: match msg {
|
||||
Some(msg) => ClientRequestMethod::servermsg(msg),
|
||||
None => ClientRequestMethod::serverclose(ServerClosedParams { i }),
|
||||
},
|
||||
}))
|
||||
.await
|
||||
}
|
||||
ServerMessageDestination::Rpc(caller) => {
|
||||
match msg {
|
||||
Some(msg) => caller.notify("servermsg", msg),
|
||||
None => caller.notify("serverclose", ServerClosedParams { i }),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
if let Some(b) = body_or_end {
|
||||
let body = self.get_server_msg_content(b, false);
|
||||
let r =
|
||||
send_data_or_close_if_none(i, &mut tx, Some(RefServerMessageParams { i, body }))
|
||||
.await;
|
||||
self.tx = Some(tx);
|
||||
return r;
|
||||
}
|
||||
|
||||
let tail = self.get_server_msg_content(&[], true);
|
||||
if !tail.is_empty() {
|
||||
let _ = send_data_or_close_if_none(
|
||||
i,
|
||||
&mut tx,
|
||||
Some(RefServerMessageParams { i, body: tail }),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
let r = send_data_or_close_if_none(i, &mut tx, None).await;
|
||||
self.tx = Some(tx);
|
||||
r
|
||||
}
|
||||
|
||||
pub(crate) fn get_server_msg_content<'a: 'b, 'b>(&'a mut self, body: &'b [u8]) -> &'b [u8] {
|
||||
pub(crate) fn get_server_msg_content<'a: 'b, 'b>(
|
||||
&'a mut self,
|
||||
body: &'b [u8],
|
||||
finish: bool,
|
||||
) -> &'b [u8] {
|
||||
if let Some(flate) = &mut self.flate {
|
||||
if let Ok(compressed) = flate.process(body) {
|
||||
if let Ok(compressed) = flate.process(body, finish) {
|
||||
return compressed;
|
||||
}
|
||||
}
|
||||
@@ -137,6 +138,32 @@ impl ServerMessageSink {
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_data_or_close_if_none(
|
||||
i: u16,
|
||||
tx: &mut ServerMessageDestination,
|
||||
msg: Option<RefServerMessageParams<'_>>,
|
||||
) -> Result<(), mpsc::error::SendError<SocketSignal>> {
|
||||
match tx {
|
||||
ServerMessageDestination::Channel(tx) => {
|
||||
tx.send(SocketSignal::from_message(&ToClientRequest {
|
||||
id: None,
|
||||
params: match msg {
|
||||
Some(msg) => ClientRequestMethod::servermsg(msg),
|
||||
None => ClientRequestMethod::serverclose(ServerClosedParams { i }),
|
||||
},
|
||||
}))
|
||||
.await
|
||||
}
|
||||
ServerMessageDestination::Rpc(caller) => {
|
||||
match msg {
|
||||
Some(msg) => caller.notify("servermsg", msg),
|
||||
None => caller.notify("serverclose", ServerClosedParams { i }),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ServerMessageSink {
|
||||
fn drop(&mut self) {
|
||||
self.multiplexer.remove(self.id);
|
||||
@@ -162,7 +189,8 @@ impl ClientMessageDecoder {
|
||||
|
||||
pub fn decode<'a: 'b, 'b>(&'a mut self, message: &'b [u8]) -> std::io::Result<&'b [u8]> {
|
||||
match &mut self.dec {
|
||||
Some(d) => d.process(message),
|
||||
// todo@connor4312 do we ever need to actually 'finish' the client message stream?
|
||||
Some(d) => d.process(message, false),
|
||||
None => Ok(message),
|
||||
}
|
||||
}
|
||||
@@ -175,6 +203,7 @@ trait FlateAlgorithm {
|
||||
&mut self,
|
||||
contents: &[u8],
|
||||
output: &mut [u8],
|
||||
finish: bool,
|
||||
) -> Result<flate2::Status, std::io::Error>;
|
||||
}
|
||||
|
||||
@@ -193,9 +222,15 @@ impl FlateAlgorithm for DecompressFlateAlgorithm {
|
||||
&mut self,
|
||||
contents: &[u8],
|
||||
output: &mut [u8],
|
||||
finish: bool,
|
||||
) -> Result<flate2::Status, std::io::Error> {
|
||||
let mode = match finish {
|
||||
true => flate2::FlushDecompress::Finish,
|
||||
false => flate2::FlushDecompress::None,
|
||||
};
|
||||
|
||||
self.0
|
||||
.decompress(contents, output, flate2::FlushDecompress::None)
|
||||
.decompress(contents, output, mode)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
|
||||
}
|
||||
}
|
||||
@@ -215,9 +250,15 @@ impl FlateAlgorithm for CompressFlateAlgorithm {
|
||||
&mut self,
|
||||
contents: &[u8],
|
||||
output: &mut [u8],
|
||||
finish: bool,
|
||||
) -> Result<flate2::Status, std::io::Error> {
|
||||
let mode = match finish {
|
||||
true => flate2::FlushCompress::Finish,
|
||||
false => flate2::FlushCompress::Sync,
|
||||
};
|
||||
|
||||
self.0
|
||||
.compress(contents, output, flate2::FlushCompress::Sync)
|
||||
.compress(contents, output, mode)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
|
||||
}
|
||||
}
|
||||
@@ -241,23 +282,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&mut self, contents: &[u8]) -> std::io::Result<&[u8]> {
|
||||
pub fn process(&mut self, contents: &[u8], finish: bool) -> std::io::Result<&[u8]> {
|
||||
let mut out_offset = 0;
|
||||
let mut in_offset = 0;
|
||||
loop {
|
||||
let in_before = self.flate.total_in();
|
||||
let out_before = self.flate.total_out();
|
||||
|
||||
match self
|
||||
.flate
|
||||
.process(&contents[in_offset..], &mut self.output[out_offset..])
|
||||
{
|
||||
match self.flate.process(
|
||||
&contents[in_offset..],
|
||||
&mut self.output[out_offset..],
|
||||
finish,
|
||||
) {
|
||||
Ok(flate2::Status::Ok | flate2::Status::BufError) => {
|
||||
let processed_len = in_offset + (self.flate.total_in() - in_before) as usize;
|
||||
let output_len = out_offset + (self.flate.total_out() - out_before) as usize;
|
||||
if processed_len < contents.len() {
|
||||
if processed_len < contents.len() || output_len == self.output.len() {
|
||||
// If we filled the output buffer but there's more data to compress,
|
||||
// extend the output buffer and keep compressing.
|
||||
// or the output got filled after processing all input, extend
|
||||
// the output buffer and keep compressing.
|
||||
out_offset = output_len;
|
||||
in_offset = processed_len;
|
||||
if output_len == self.output.len() {
|
||||
@@ -298,7 +341,7 @@ mod tests {
|
||||
// 3000 and 30000 test resizing the buffer
|
||||
for msg_len in [3, 30, 300, 3000, 30000] {
|
||||
let vals = (0..msg_len).map(|v| v as u8).collect::<Vec<u8>>();
|
||||
let compressed = sink.get_server_msg_content(&vals);
|
||||
let compressed = sink.get_server_msg_content(&vals, false);
|
||||
assert_ne!(compressed, vals);
|
||||
let decompressed = decompress.decode(compressed).unwrap();
|
||||
assert_eq!(decompressed.len(), vals.len());
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.88.0",
|
||||
"distro": "de07f23454d5352cc3711ca34d51278767e6eb0a",
|
||||
"distro": "a5b6daf94540aab9d17335c2c2533e629d750123",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
||||
@@ -140,7 +140,7 @@ export class HighlightedLabel extends Disposable {
|
||||
} else {
|
||||
if (!this.customHover && this.title !== '') {
|
||||
const hoverDelegate = this.options?.hoverDelegate ?? getDefaultHoverDelegate('mouse');
|
||||
this.customHover = this._store.add(setupCustomHover(hoverDelegate, this.domNode, this.title));
|
||||
this.customHover = this._register(setupCustomHover(hoverDelegate, this.domNode, this.title));
|
||||
} else if (this.customHover) {
|
||||
this.customHover.update(this.title);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { reset } from 'vs/base/browser/dom';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class SimpleIconLabel {
|
||||
export class SimpleIconLabel implements IDisposable {
|
||||
|
||||
private hover?: ICustomHover;
|
||||
|
||||
constructor(
|
||||
private readonly _container: HTMLElement
|
||||
@@ -17,6 +22,14 @@ export class SimpleIconLabel {
|
||||
}
|
||||
|
||||
set title(title: string) {
|
||||
this._container.title = title;
|
||||
if (!this.hover && title) {
|
||||
this.hover = setupCustomHover(getDefaultHoverDelegate('mouse'), this._container, title);
|
||||
} else if (this.hover) {
|
||||
this.hover.update(title);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.hover?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { UILabelProvider } from 'vs/base/common/keybindingLabels';
|
||||
import { ResolvedKeybinding, ResolvedChord } from 'vs/base/common/keybindings';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import 'vs/css!./keybindingLabel';
|
||||
@@ -50,18 +53,21 @@ export const unthemedKeybindingLabelOptions: KeybindingLabelOptions = {
|
||||
keybindingLabelShadow: undefined
|
||||
};
|
||||
|
||||
export class KeybindingLabel {
|
||||
export class KeybindingLabel extends Disposable {
|
||||
|
||||
private domNode: HTMLElement;
|
||||
private options: KeybindingLabelOptions;
|
||||
|
||||
private readonly keyElements = new Set<HTMLSpanElement>();
|
||||
|
||||
private hover: ICustomHover;
|
||||
private keybinding: ResolvedKeybinding | undefined;
|
||||
private matches: Matches | undefined;
|
||||
private didEverRender: boolean;
|
||||
|
||||
constructor(container: HTMLElement, private os: OperatingSystem, options?: KeybindingLabelOptions) {
|
||||
super();
|
||||
|
||||
this.options = options || Object.create(null);
|
||||
|
||||
const labelForeground = this.options.keybindingLabelForeground;
|
||||
@@ -71,6 +77,8 @@ export class KeybindingLabel {
|
||||
this.domNode.style.color = labelForeground;
|
||||
}
|
||||
|
||||
this.hover = this._register(setupCustomHover(getDefaultHoverDelegate('mouse'), this.domNode, ''));
|
||||
|
||||
this.didEverRender = false;
|
||||
container.appendChild(this.domNode);
|
||||
}
|
||||
@@ -102,11 +110,8 @@ export class KeybindingLabel {
|
||||
this.renderChord(this.domNode, chords[i], this.matches ? this.matches.chordPart : null);
|
||||
}
|
||||
const title = (this.options.disableTitle ?? false) ? undefined : this.keybinding.getAriaLabel() || undefined;
|
||||
if (title !== undefined) {
|
||||
this.domNode.title = title;
|
||||
} else {
|
||||
this.domNode.removeAttribute('title');
|
||||
}
|
||||
this.hover.update(title);
|
||||
this.domNode.setAttribute('aria-label', title || '');
|
||||
} else if (this.options && this.options.renderUnboundKeybindings) {
|
||||
this.renderUnbound(this.domNode);
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ export class ToggleActionViewItem extends BaseActionViewItem {
|
||||
inputActiveOptionBackground: options.toggleStyles?.inputActiveOptionBackground,
|
||||
inputActiveOptionBorder: options.toggleStyles?.inputActiveOptionBorder,
|
||||
inputActiveOptionForeground: options.toggleStyles?.inputActiveOptionForeground,
|
||||
hoverDelegate: options.hoverDelegate
|
||||
}));
|
||||
this._register(this.toggle.onChange(() => this._action.checked = !!this.toggle && this.toggle.checked));
|
||||
}
|
||||
|
||||
@@ -389,12 +389,12 @@ export class ObservableValue<T, TChange = void>
|
||||
constructor(
|
||||
private readonly _owner: Owner,
|
||||
private readonly _debugName: string | undefined,
|
||||
initialValue: T
|
||||
initialValue: T,
|
||||
) {
|
||||
super();
|
||||
this._value = initialValue;
|
||||
}
|
||||
public get(): T {
|
||||
public override get(): T {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,6 +253,9 @@ class ObservableSignal<TChange> extends BaseObservable<void, TChange> implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `debouncedObservable2` instead.
|
||||
*/
|
||||
export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: number, disposableStore: DisposableStore): IObservable<T | undefined> {
|
||||
const debouncedObservable = observableValue<T | undefined>('debounced', undefined);
|
||||
|
||||
@@ -276,6 +279,48 @@ export function debouncedObservable<T>(observable: IObservable<T>, debounceMs: n
|
||||
return debouncedObservable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable that debounces the input observable.
|
||||
*/
|
||||
export function debouncedObservable2<T>(observable: IObservable<T>, debounceMs: number): IObservable<T> {
|
||||
let hasValue = false;
|
||||
let lastValue: T | undefined;
|
||||
|
||||
let timeout: any = undefined;
|
||||
|
||||
return observableFromEvent<T, void>(cb => {
|
||||
const d = autorun(reader => {
|
||||
const value = observable.read(reader);
|
||||
|
||||
if (!hasValue) {
|
||||
hasValue = true;
|
||||
lastValue = value;
|
||||
} else {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => {
|
||||
lastValue = value;
|
||||
cb();
|
||||
}, debounceMs);
|
||||
}
|
||||
});
|
||||
return {
|
||||
dispose() {
|
||||
d.dispose();
|
||||
hasValue = false;
|
||||
lastValue = undefined;
|
||||
},
|
||||
};
|
||||
}, () => {
|
||||
if (hasValue) {
|
||||
return lastValue!;
|
||||
} else {
|
||||
return observable.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function wasEventTriggeredRecently(event: Event<any>, timeoutMs: number, disposableStore: DisposableStore): IObservable<boolean> {
|
||||
const observable = observableValue('triggeredRecently', false);
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ export class DiffEditorItemTemplate extends Disposable implements IPooledObject<
|
||||
});
|
||||
}
|
||||
|
||||
private readonly _headerHeight = /*this._elements.header.clientHeight*/ 48;
|
||||
private readonly _headerHeight = /*this._elements.header.clientHeight*/ 40;
|
||||
|
||||
private _lastScrollTop = -1;
|
||||
private _isSettingScrollTop = false;
|
||||
@@ -285,6 +285,6 @@ function isFocused(editor: ICodeEditor): IObservable<boolean> {
|
||||
store.add(editor.onDidBlurEditorWidget(() => h(false)));
|
||||
return store;
|
||||
},
|
||||
() => editor.hasWidgetFocus()
|
||||
() => editor.hasTextFocus()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
.header-content {
|
||||
margin: 8px 8px 0px 8px;
|
||||
padding: 8px 5px;
|
||||
padding: 4px 5px;
|
||||
|
||||
border-top: 1px solid var(--vscode-multiDiffEditor-border);
|
||||
border-right: 1px solid var(--vscode-multiDiffEditor-border);
|
||||
|
||||
@@ -1710,6 +1710,14 @@ export interface CommentInfo {
|
||||
commentingRanges: CommentingRanges;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CommentingRangeResourceHint {
|
||||
schemes: readonly string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -1051,6 +1051,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
|
||||
icon: findSelectionIcon,
|
||||
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
|
||||
isChecked: false,
|
||||
hoverDelegate: hoverDelegate,
|
||||
inputActiveOptionBackground: asCssVariable(inputActiveOptionBackground),
|
||||
inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder),
|
||||
inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground),
|
||||
|
||||
@@ -302,7 +302,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem {
|
||||
if (this.label) {
|
||||
const div = h('div.keybinding').root;
|
||||
|
||||
const k = new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions });
|
||||
const k = this._register(new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions }));
|
||||
k.set(kb);
|
||||
this.label.textContent = this._action.label;
|
||||
this.label.appendChild(div);
|
||||
|
||||
@@ -121,7 +121,7 @@ export class InlineEditController extends Disposable {
|
||||
if (this._configurationService.getValue('editor.experimentalInlineEdit.keepOnBlur') || editor.getOption(EditorOption.inlineEdit).keepOnBlur) {
|
||||
return;
|
||||
}
|
||||
this._currentRequestCts?.dispose();
|
||||
this._currentRequestCts?.dispose(true);
|
||||
this._currentRequestCts = undefined;
|
||||
this.clear(false);
|
||||
}));
|
||||
|
||||
@@ -175,7 +175,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem {
|
||||
if (this.label) {
|
||||
const div = h('div.keybinding').root;
|
||||
|
||||
const k = new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions });
|
||||
const k = this._register(new KeybindingLabel(div, OS, { disableTitle: true, ...unthemedKeybindingLabelOptions }));
|
||||
k.set(kb);
|
||||
this.label.textContent = this._action.label;
|
||||
this.label.appendChild(div);
|
||||
|
||||
@@ -140,7 +140,7 @@ class ActionItemRenderer<T> implements IListRenderer<IActionListItem<T>, IAction
|
||||
}
|
||||
|
||||
disposeTemplate(_templateData: IActionMenuTemplateData): void {
|
||||
// noop
|
||||
_templateData.keybinding.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,6 +277,7 @@ class ListElementRenderer implements IListRenderer<IListElement, IListElementTem
|
||||
// Keybinding
|
||||
const keybindingContainer = dom.append(row1, $('.quick-input-list-entry-keybinding'));
|
||||
data.keybinding = new KeybindingLabel(keybindingContainer, platform.OS);
|
||||
data.toDisposeTemplate.push(data.keybinding);
|
||||
|
||||
// Detail
|
||||
const detailContainer = dom.append(row2, $('.quick-input-list-label-meta'));
|
||||
|
||||
@@ -515,8 +515,6 @@ class WindowsPtyHeuristics extends Disposable {
|
||||
|
||||
private _onCursorMoveListener = this._register(new MutableDisposable());
|
||||
|
||||
private _recentlyPerformedCsiJ = false;
|
||||
|
||||
private _tryAdjustCommandStartMarkerScheduler?: RunOnceScheduler;
|
||||
private _tryAdjustCommandStartMarkerScannedLineCount: number = 0;
|
||||
private _tryAdjustCommandStartMarkerPollCount: number = 0;
|
||||
@@ -530,8 +528,8 @@ class WindowsPtyHeuristics extends Disposable {
|
||||
super();
|
||||
|
||||
this._register(_terminal.parser.registerCsiHandler({ final: 'J' }, params => {
|
||||
// Clear commands when the viewport is cleared
|
||||
if (params.length >= 1 && (params[0] === 2 || params[0] === 3)) {
|
||||
this._recentlyPerformedCsiJ = true;
|
||||
this._hooks.clearCommandsInViewport();
|
||||
}
|
||||
// We don't want to override xterm.js' default behavior, just augment it
|
||||
@@ -539,11 +537,6 @@ class WindowsPtyHeuristics extends Disposable {
|
||||
}));
|
||||
|
||||
this._register(this._capability.onBeforeCommandFinished(command => {
|
||||
if (this._recentlyPerformedCsiJ) {
|
||||
this._recentlyPerformedCsiJ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// For older Windows backends we cannot listen to CSI J, instead we assume running clear
|
||||
// or cls will clear all commands in the viewport. This is not perfect but it's right
|
||||
// most of the time.
|
||||
|
||||
@@ -370,8 +370,8 @@ export class MainThreadCommentController implements ICommentController {
|
||||
}
|
||||
}
|
||||
|
||||
updateCommentingRanges() {
|
||||
this._commentService.updateCommentingRanges(this._uniqueId);
|
||||
updateCommentingRanges(resourceHints?: languages.CommentingRangeResourceHint) {
|
||||
this._commentService.updateCommentingRanges(this._uniqueId, resourceHints);
|
||||
}
|
||||
|
||||
private getKnownThread(commentThreadHandle: number): MainThreadCommentThread<IRange | ICellRange> {
|
||||
@@ -591,14 +591,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
return provider.deleteCommentThread(commentThreadHandle);
|
||||
}
|
||||
|
||||
$updateCommentingRanges(handle: number) {
|
||||
$updateCommentingRanges(handle: number, resourceHints?: languages.CommentingRangeResourceHint) {
|
||||
const provider = this._commentControllers.get(handle);
|
||||
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
provider.updateCommentingRanges();
|
||||
provider.updateCommentingRanges(resourceHints);
|
||||
}
|
||||
|
||||
private registerView(commentsViewAlreadyRegistered: boolean) {
|
||||
|
||||
@@ -145,7 +145,7 @@ export interface MainThreadCommentsShape extends IDisposable {
|
||||
$createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange | ICellRange | undefined, extensionId: ExtensionIdentifier, isTemplate: boolean): languages.CommentThread<IRange | ICellRange> | undefined;
|
||||
$updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, changes: CommentThreadChanges): void;
|
||||
$deleteCommentThread(handle: number, commentThreadHandle: number): void;
|
||||
$updateCommentingRanges(handle: number): void;
|
||||
$updateCommentingRanges(handle: number, resourceHints?: languages.CommentingRangeResourceHint): void;
|
||||
}
|
||||
|
||||
export interface AuthenticationForceNewSessionOptions {
|
||||
|
||||
@@ -565,7 +565,10 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo
|
||||
|
||||
set commentingRangeProvider(provider: vscode.CommentingRangeProvider | undefined) {
|
||||
this._commentingRangeProvider = provider;
|
||||
proxy.$updateCommentingRanges(this.handle);
|
||||
if (provider?.resourceHints) {
|
||||
checkProposedApiEnabled(this._extension, 'commentingRangeHint');
|
||||
}
|
||||
proxy.$updateCommentingRanges(this.handle, provider?.resourceHints);
|
||||
}
|
||||
|
||||
private _reactionHandler?: ReactionHandler;
|
||||
|
||||
@@ -517,7 +517,7 @@ export class BreadcrumbsControl {
|
||||
this._widget.setSelection(items[idx + 1], BreadcrumbsControl.Payload_Pick);
|
||||
}
|
||||
} else {
|
||||
element.outline.reveal(element, { pinned }, group === SIDE_GROUP);
|
||||
element.outline.reveal(element, { pinned }, group === SIDE_GROUP, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,7 +860,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
return (<IOutline<any>>input).reveal(element, {
|
||||
pinned: true,
|
||||
preserveFocus: false
|
||||
}, true);
|
||||
}, true, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -507,7 +507,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
|
||||
protected async _revealElement(element: any, options: IEditorOptions, sideBySide: boolean): Promise<boolean> {
|
||||
this._onWillPickElement.fire();
|
||||
const outline: IOutline<any> = this._tree.getInput();
|
||||
await outline.reveal(element, options, sideBySide);
|
||||
await outline.reveal(element, options, sideBySide, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export class EditorGroupWatermark extends Disposable {
|
||||
private readonly transientDisposables = this._register(new DisposableStore());
|
||||
private enabled: boolean = false;
|
||||
private workbenchState: WorkbenchState;
|
||||
private keybindingLabel?: KeybindingLabel;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@@ -145,8 +146,9 @@ export class EditorGroupWatermark extends Disposable {
|
||||
const dt = append(dl, $('dt'));
|
||||
dt.textContent = entry.text;
|
||||
const dd = append(dl, $('dd'));
|
||||
const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
|
||||
keybinding.set(keys);
|
||||
this.keybindingLabel?.dispose();
|
||||
this.keybindingLabel = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true, ...defaultKeybindingLabelStyles });
|
||||
this.keybindingLabel.set(keys);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,5 +164,6 @@ export class EditorGroupWatermark extends Disposable {
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
this.clear();
|
||||
this.keybindingLabel?.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export abstract class EditorPlaceholder extends EditorPane {
|
||||
|
||||
// Icon
|
||||
const iconContainer = container.appendChild($('.editor-placeholder-icon-container'));
|
||||
const iconWidget = new SimpleIconLabel(iconContainer);
|
||||
const iconWidget = disposables.add(new SimpleIconLabel(iconContainer));
|
||||
iconWidget.text = icon;
|
||||
|
||||
// Label
|
||||
|
||||
@@ -73,7 +73,7 @@ export class StatusbarEntryItem extends Disposable {
|
||||
this._register(Gesture.addTarget(this.labelContainer)); // enable touch
|
||||
|
||||
// Label (with support for progress)
|
||||
this.label = new StatusBarCodiconLabel(this.labelContainer);
|
||||
this.label = this._register(new StatusBarCodiconLabel(this.labelContainer));
|
||||
this.container.appendChild(this.labelContainer);
|
||||
|
||||
// Beak Container
|
||||
|
||||
+4
-2
@@ -34,6 +34,8 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration';
|
||||
import { LOG_MODE_ID, OUTPUT_MODE_ID } from 'vs/workbench/services/output/common/output';
|
||||
import { SEARCH_RESULT_LANGUAGE_ID } from 'vs/workbench/services/search/common/search';
|
||||
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
@@ -263,7 +265,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget {
|
||||
|
||||
hintElement.appendChild(before);
|
||||
|
||||
const label = new KeybindingLabel(hintElement, OS);
|
||||
const label = hintHandler.disposables.add(new KeybindingLabel(hintElement, OS));
|
||||
label.set(keybindingHint);
|
||||
label.element.style.width = 'min-content';
|
||||
label.element.style.display = 'inline';
|
||||
@@ -382,7 +384,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget {
|
||||
anchor.style.cursor = 'pointer';
|
||||
const id = keybindingsLookup.shift();
|
||||
const title = id && this.keybindingService.lookupKeybinding(id)?.getLabel();
|
||||
anchor.title = title ?? '';
|
||||
hintHandler.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), anchor, title ?? ''));
|
||||
}
|
||||
|
||||
return { hintElement, ariaLabel };
|
||||
|
||||
@@ -219,7 +219,7 @@ class DocumentSymbolsOutline implements IOutline<DocumentSymbolItem> {
|
||||
return this._outlineModel?.uri;
|
||||
}
|
||||
|
||||
async reveal(entry: DocumentSymbolItem, options: IEditorOptions, sideBySide: boolean): Promise<void> {
|
||||
async reveal(entry: DocumentSymbolItem, options: IEditorOptions, sideBySide: boolean, select: boolean): Promise<void> {
|
||||
const model = OutlineModel.get(entry);
|
||||
if (!model || !(entry instanceof OutlineElement)) {
|
||||
return;
|
||||
@@ -228,7 +228,7 @@ class DocumentSymbolsOutline implements IOutline<DocumentSymbolItem> {
|
||||
resource: model.uri,
|
||||
options: {
|
||||
...options,
|
||||
selection: Range.collapseToStart(entry.symbol.selectionRange),
|
||||
selection: select ? entry.symbol.range : Range.collapseToStart(entry.symbol.selectionRange),
|
||||
selectionRevealType: TextEditorSelectionRevealType.NearTopIfOutsideViewport,
|
||||
}
|
||||
}, this._editor, sideBySide);
|
||||
|
||||
@@ -183,7 +183,7 @@ export class GotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccess
|
||||
picker.hide();
|
||||
const [entry] = picker.selectedItems;
|
||||
if (entry && entries[entry.index]) {
|
||||
outline.reveal(entries[entry.index].element, {}, false);
|
||||
outline.reveal(entries[entry.index].element, {}, false, false);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread } from 'vs/editor/common/languages';
|
||||
import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread, CommentingRangeResourceHint } from 'vs/editor/common/languages';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -104,7 +104,7 @@ export interface ICommentService {
|
||||
disposeCommentThread(ownerId: string, threadId: string): void;
|
||||
getDocumentComments(resource: URI): Promise<(ICommentInfo | null)[]>;
|
||||
getNotebookComments(resource: URI): Promise<(INotebookCommentInfo | null)[]>;
|
||||
updateCommentingRanges(ownerId: string): void;
|
||||
updateCommentingRanges(ownerId: string, resourceHints?: CommentingRangeResourceHint): void;
|
||||
hasReactionHandler(owner: string): boolean;
|
||||
toggleReaction(owner: string, resource: URI, thread: CommentThread<IRange | ICellRange>, comment: Comment, reaction: CommentReaction): Promise<void>;
|
||||
setActiveEditingCommentThread(commentThread: CommentThread<IRange | ICellRange> | null): void;
|
||||
@@ -172,6 +172,7 @@ export class CommentService extends Disposable implements ICommentService {
|
||||
public readonly commentsModel: ICommentsModel = this._commentsModel;
|
||||
|
||||
private _commentingRangeResources = new Set<string>(); // URIs
|
||||
private _commentingRangeResourceHintSchemes = new Set<string>(); // schemes
|
||||
|
||||
constructor(
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@@ -406,7 +407,12 @@ export class CommentService extends Disposable implements ICommentService {
|
||||
this._onDidUpdateNotebookCommentThreads.fire(evt);
|
||||
}
|
||||
|
||||
updateCommentingRanges(ownerId: string) {
|
||||
updateCommentingRanges(ownerId: string, resourceHints?: CommentingRangeResourceHint) {
|
||||
if (resourceHints?.schemes && resourceHints.schemes.length > 0) {
|
||||
for (const scheme of resourceHints.schemes) {
|
||||
this._commentingRangeResourceHintSchemes.add(scheme);
|
||||
}
|
||||
}
|
||||
this._workspaceHasCommenting.set(true);
|
||||
this._onDidUpdateCommentingRanges.fire({ owner: ownerId });
|
||||
}
|
||||
@@ -518,6 +524,6 @@ export class CommentService extends Disposable implements ICommentService {
|
||||
}
|
||||
|
||||
resourceHasCommentingRanges(resource: URI): boolean {
|
||||
return this._commentingRangeResources.has(resource.toString());
|
||||
return this._commentingRangeResourceHintSchemes.has(resource.scheme) || this._commentingRangeResources.has(resource.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +376,7 @@ export class CommentController implements IEditorContribution {
|
||||
private _commentThreadRangeDecorator!: CommentThreadRangeDecorator;
|
||||
private mouseDownInfo: { lineNumber: number } | null = null;
|
||||
private _commentingRangeSpaceReserved = false;
|
||||
private _commentingRangeAmountReserved = 0;
|
||||
private _computePromise: CancelablePromise<Array<ICommentInfo | null>> | null;
|
||||
private _addInProgress!: boolean;
|
||||
private _emptyThreadsToAddQueue: [Range | undefined, IEditorMouseEvent | undefined][] = [];
|
||||
@@ -1179,15 +1180,20 @@ export class CommentController implements IEditorContribution {
|
||||
return { extraEditorClassName, lineDecorationsWidth };
|
||||
}
|
||||
|
||||
private getWithCommentsEditorOptions(editor: ICodeEditor, extraEditorClassName: string[], startingLineDecorationsWidth: number) {
|
||||
private getWithCommentsLineDecorationWidth(editor: ICodeEditor, startingLineDecorationsWidth: number) {
|
||||
let lineDecorationsWidth = startingLineDecorationsWidth;
|
||||
const options = editor.getOptions();
|
||||
if (options.get(EditorOption.folding) && options.get(EditorOption.showFoldingControls) !== 'never') {
|
||||
lineDecorationsWidth -= 11;
|
||||
}
|
||||
lineDecorationsWidth += 24;
|
||||
this._commentingRangeAmountReserved = lineDecorationsWidth;
|
||||
return this._commentingRangeAmountReserved;
|
||||
}
|
||||
|
||||
private getWithCommentsEditorOptions(editor: ICodeEditor, extraEditorClassName: string[], startingLineDecorationsWidth: number) {
|
||||
extraEditorClassName.push('inline-comment');
|
||||
return { lineDecorationsWidth, extraEditorClassName };
|
||||
return { lineDecorationsWidth: this.getWithCommentsLineDecorationWidth(editor, startingLineDecorationsWidth), extraEditorClassName };
|
||||
}
|
||||
|
||||
private updateEditorLayoutOptions(editor: ICodeEditor, extraEditorClassName: string[], lineDecorationsWidth: number) {
|
||||
@@ -1197,6 +1203,15 @@ export class CommentController implements IEditorContribution {
|
||||
});
|
||||
}
|
||||
|
||||
private ensureCommentingRangeReservedAmount(editor: ICodeEditor) {
|
||||
const existing = this.getExistingCommentEditorOptions(editor);
|
||||
if (existing.lineDecorationsWidth !== this._commentingRangeAmountReserved) {
|
||||
editor.updateOptions({
|
||||
lineDecorationsWidth: this.getWithCommentsLineDecorationWidth(editor, existing.lineDecorationsWidth)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private tryUpdateReservedSpace(uri?: URI) {
|
||||
if (!this.editor) {
|
||||
return;
|
||||
@@ -1211,11 +1226,15 @@ export class CommentController implements IEditorContribution {
|
||||
|
||||
const hasCommentsOrRanges = hasCommentsOrRangesInInfo || resourceHasCommentingRanges;
|
||||
|
||||
if (hasCommentsOrRanges && !this._commentingRangeSpaceReserved && this.commentService.isCommentingEnabled) {
|
||||
this._commentingRangeSpaceReserved = true;
|
||||
const { lineDecorationsWidth, extraEditorClassName } = this.getExistingCommentEditorOptions(this.editor);
|
||||
const newOptions = this.getWithCommentsEditorOptions(this.editor, extraEditorClassName, lineDecorationsWidth);
|
||||
this.updateEditorLayoutOptions(this.editor, newOptions.extraEditorClassName, newOptions.lineDecorationsWidth);
|
||||
if (hasCommentsOrRanges && this.commentService.isCommentingEnabled) {
|
||||
if (!this._commentingRangeSpaceReserved) {
|
||||
this._commentingRangeSpaceReserved = true;
|
||||
const { lineDecorationsWidth, extraEditorClassName } = this.getExistingCommentEditorOptions(this.editor);
|
||||
const newOptions = this.getWithCommentsEditorOptions(this.editor, extraEditorClassName, lineDecorationsWidth);
|
||||
this.updateEditorLayoutOptions(this.editor, newOptions.extraEditorClassName, newOptions.lineDecorationsWidth);
|
||||
} else {
|
||||
this.ensureCommentingRangeReservedAmount(this.editor);
|
||||
}
|
||||
} else if ((!hasCommentsOrRanges || !this.commentService.isCommentingEnabled) && this._commentingRangeSpaceReserved) {
|
||||
this._commentingRangeSpaceReserved = false;
|
||||
const { lineDecorationsWidth, extraEditorClassName } = this.getExistingCommentEditorOptions(this.editor);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { $, append, clearNode } from 'vs/base/browser/dom';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { ExtensionIdentifier, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -447,16 +447,18 @@ class ExtensionFeatureView extends Disposable {
|
||||
|
||||
private renderTableData(container: HTMLElement, renderer: IExtensionFeatureTableRenderer): void {
|
||||
const tableData = this._register(renderer.render(this.manifest));
|
||||
const tableDisposable = this._register(new MutableDisposable());
|
||||
if (tableData.onDidChange) {
|
||||
this._register(tableData.onDidChange(data => {
|
||||
clearNode(container);
|
||||
this.renderTable(data, container);
|
||||
tableDisposable.value = this.renderTable(data, container);
|
||||
}));
|
||||
}
|
||||
this.renderTable(tableData.data, container);
|
||||
tableDisposable.value = this.renderTable(tableData.data, container);
|
||||
}
|
||||
|
||||
private renderTable(tableData: ITableData, container: HTMLElement): void {
|
||||
private renderTable(tableData: ITableData, container: HTMLElement): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
append(container,
|
||||
$('table', undefined,
|
||||
$('tr', undefined,
|
||||
@@ -478,7 +480,7 @@ class ExtensionFeatureView extends Disposable {
|
||||
result.push(element);
|
||||
} else if (item instanceof ResolvedKeybinding) {
|
||||
const element = $('');
|
||||
const kbl = new KeybindingLabel(element, OS, defaultKeybindingLabelStyles);
|
||||
const kbl = disposables.add(new KeybindingLabel(element, OS, defaultKeybindingLabelStyles));
|
||||
kbl.set(item);
|
||||
result.push(element);
|
||||
} else if (item instanceof Color) {
|
||||
@@ -490,6 +492,7 @@ class ExtensionFeatureView extends Disposable {
|
||||
})
|
||||
);
|
||||
})));
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private renderMarkdownData(container: HTMLElement, renderer: IExtensionFeatureMarkdownRenderer): void {
|
||||
|
||||
@@ -52,7 +52,7 @@ import { TelemetryTrustedValue } from 'vs/platform/telemetry/common/telemetryUti
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService, IPromptButton } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
interface IExtensionStateProvider<T> {
|
||||
(extension: Extension): T;
|
||||
@@ -1650,17 +1650,19 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
}
|
||||
|
||||
async install(arg: string | URI | IExtension, installOptions: InstallExtensionOptions = {}, progressLocation?: ProgressLocation): Promise<IExtension> {
|
||||
let installable: URI | { extension: IExtension; gallery: IGalleryExtension };
|
||||
let installable: URI | IGalleryExtension | undefined;
|
||||
let extension: IExtension | undefined;
|
||||
|
||||
if (arg instanceof URI) {
|
||||
installable = arg;
|
||||
} else {
|
||||
let installableInfo: IExtensionInfo | undefined;
|
||||
let gallery: IGalleryExtension | undefined;
|
||||
let extension: IExtension | undefined;
|
||||
if (isString(arg)) {
|
||||
installableInfo = { id: arg, version: installOptions.version, preRelease: installOptions?.installPreReleaseVersion ?? this.preferPreReleases };
|
||||
extension = this.local.find(e => areSameExtensions(e.identifier, { id: arg }));
|
||||
if (!extension?.isBuiltin) {
|
||||
installableInfo = { id: arg, version: installOptions.version, preRelease: installOptions.installPreReleaseVersion ?? this.preferPreReleases };
|
||||
}
|
||||
} else {
|
||||
extension = arg;
|
||||
gallery = arg.gallery;
|
||||
@@ -1672,47 +1674,44 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
const targetPlatform = extension?.server ? await extension.server.extensionManagementService.getTargetPlatform() : undefined;
|
||||
gallery = firstOrDefault(await this.galleryService.getExtensions([installableInfo], { targetPlatform }, CancellationToken.None));
|
||||
}
|
||||
if (!gallery) {
|
||||
const id = isString(arg) ? arg : (<IExtension>arg).identifier.id;
|
||||
if (installOptions.version) {
|
||||
throw new Error(nls.localize('not found version', "Unable to install extension '{0}' because the requested version '{1}' is not found.", id, installOptions.version));
|
||||
} else {
|
||||
throw new Error(nls.localize('not found', "Unable to install extension '{0}' because it is not found.", id));
|
||||
}
|
||||
}
|
||||
if (!extension) {
|
||||
if (!extension && gallery) {
|
||||
extension = this.instantiationService.createInstance(Extension, ext => this.getExtensionState(ext), ext => this.getReloadStatus(ext), undefined, undefined, gallery);
|
||||
Extensions.updateExtensionFromControlManifest(extension as Extension, await this.extensionManagementService.getExtensionsControlManifest());
|
||||
}
|
||||
if (extension?.isMalicious) {
|
||||
throw new Error(nls.localize('malicious', "This extension is reported to be problematic."));
|
||||
}
|
||||
installable = { extension, gallery };
|
||||
if (installOptions.version) {
|
||||
installOptions.installGivenVersion = true;
|
||||
// Do not install if requested to enable and extension is already installed
|
||||
if (!(installOptions.enable && extension?.local)) {
|
||||
if (!gallery) {
|
||||
const id = isString(arg) ? arg : (<IExtension>arg).identifier.id;
|
||||
if (installOptions.version) {
|
||||
throw new Error(nls.localize('not found version', "Unable to install extension '{0}' because the requested version '{1}' is not found.", id, installOptions.version));
|
||||
} else {
|
||||
throw new Error(nls.localize('not found', "Unable to install extension '{0}' because it is not found.", id));
|
||||
}
|
||||
}
|
||||
installable = gallery;
|
||||
if (installOptions.version) {
|
||||
installOptions.installGivenVersion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let extension: IExtension;
|
||||
if (installable instanceof URI || !(installOptions.enable && installable.extension.local)) {
|
||||
if (installable) {
|
||||
if (installOptions.justification) {
|
||||
const syncCheck = isUndefined(installOptions.isMachineScoped) && this.userDataSyncEnablementService.isEnabled() && this.userDataSyncEnablementService.isResourceEnabled(SyncResource.Extensions);
|
||||
const buttons: IPromptButton<boolean>[] = [];
|
||||
buttons.push({ label: isString(installOptions.justification) ? nls.localize({ key: 'installButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Install Extension") : nls.localize({ key: 'installButtonLabelWithAction', comment: ['&& denotes a mnemonic'] }, "&&Install Extension and {0}", installOptions.justification.action), run: () => true });
|
||||
if (!extension) {
|
||||
buttons.push({ label: nls.localize('open', "Open Extension"), run: () => { this.open(extension!); return false; } });
|
||||
}
|
||||
const result = await this.dialogService.prompt<boolean>({
|
||||
title: nls.localize('installExtensionTitle', "Install Extension"),
|
||||
message: installable instanceof URI ? nls.localize('installVSIXMessage', "Would you like to install the extension?") : nls.localize('installExtensionMessage', "Would you like to install '{0}' extension from '{1}'?", installable.extension.displayName, installable.extension.publisherDisplayName),
|
||||
message: extension ? nls.localize('installExtensionMessage', "Would you like to install '{0}' extension from '{1}'?", extension.displayName, extension.publisherDisplayName) : nls.localize('installVSIXMessage', "Would you like to install the extension?"),
|
||||
detail: isString(installOptions.justification) ? installOptions.justification : installOptions.justification.reason,
|
||||
cancelButton: true,
|
||||
buttons: [{
|
||||
label: isString(installOptions.justification) ? nls.localize({ key: 'installButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Install Extension") : nls.localize({ key: 'installButtonLabelWithAction', comment: ['&& denotes a mnemonic'] }, "&&Install Extension and {0}", installOptions.justification.action),
|
||||
run: () => true
|
||||
}, {
|
||||
label: nls.localize('open', "Open Extension"),
|
||||
run: () => {
|
||||
if (!(installable instanceof URI)) {
|
||||
this.open(installable.extension);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}],
|
||||
buttons,
|
||||
checkbox: syncCheck ? {
|
||||
label: nls.localize('sync extension', "Sync this extension"),
|
||||
checked: true,
|
||||
@@ -1725,11 +1724,15 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
installOptions.isMachineScoped = !result.checkboxChecked;
|
||||
}
|
||||
}
|
||||
extension = await this.doInstall(installable instanceof URI ? installable : installable.extension,
|
||||
() => installable instanceof URI ? this.installFromVSIX(installable, installOptions) : this.installFromGallery(installable.extension, installable.gallery, installOptions),
|
||||
progressLocation);
|
||||
} else {
|
||||
extension = installable.extension;
|
||||
if (installable instanceof URI) {
|
||||
extension = await this.doInstall(undefined, () => this.installFromVSIX(installable, installOptions), progressLocation);
|
||||
} else if (extension) {
|
||||
extension = await this.doInstall(extension, () => this.installFromGallery(extension!, installable, installOptions), progressLocation);
|
||||
}
|
||||
}
|
||||
|
||||
if (!extension) {
|
||||
throw new Error(nls.localize('unknown', "Unable to install extension"));
|
||||
}
|
||||
|
||||
if (installOptions.version) {
|
||||
@@ -1902,21 +1905,21 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension
|
||||
return extension;
|
||||
}
|
||||
|
||||
private doInstall(extension: IExtension | URI, installTask: () => Promise<ILocalExtension>, progressLocation?: ProgressLocation): Promise<IExtension> {
|
||||
const title = extension instanceof URI ? nls.localize('installing extension', 'Installing extension....') : nls.localize('installing named extension', "Installing '{0}' extension....", extension.displayName);
|
||||
private doInstall(extension: IExtension | undefined, installTask: () => Promise<ILocalExtension>, progressLocation?: ProgressLocation): Promise<IExtension> {
|
||||
const title = extension ? nls.localize('installing named extension', "Installing '{0}' extension....", extension.displayName) : nls.localize('installing extension', 'Installing extension....');
|
||||
return this.withProgress({
|
||||
location: progressLocation ?? ProgressLocation.Extensions,
|
||||
title
|
||||
}, async () => {
|
||||
try {
|
||||
if (!(extension instanceof URI)) {
|
||||
if (extension) {
|
||||
this.installing.push(extension);
|
||||
this._onChange.fire(extension);
|
||||
}
|
||||
const local = await installTask();
|
||||
return await this.waitAndGetInstalledExtension(local.identifier);
|
||||
} finally {
|
||||
if (!(extension instanceof URI)) {
|
||||
if (extension) {
|
||||
this.installing = this.installing.filter(e => e !== extension);
|
||||
// Trigger the change without passing the extension because it is replaced by a new instance.
|
||||
this._onChange.fire(undefined);
|
||||
|
||||
-3
@@ -365,7 +365,6 @@ suite('ExtensionsWorkbenchServiceTest', () => {
|
||||
const extension = page.firstPage[0];
|
||||
assert.strictEqual(ExtensionState.Uninstalled, extension.state);
|
||||
|
||||
testObject.install(extension);
|
||||
const identifier = gallery.identifier;
|
||||
|
||||
// Installing
|
||||
@@ -450,7 +449,6 @@ suite('ExtensionsWorkbenchServiceTest', () => {
|
||||
const extension = page.firstPage[0];
|
||||
assert.strictEqual(ExtensionState.Uninstalled, extension.state);
|
||||
|
||||
testObject.install(extension);
|
||||
installEvent.fire({ identifier: gallery.identifier, source: gallery });
|
||||
const promise = Event.toPromise(testObject.onChange);
|
||||
|
||||
@@ -470,7 +468,6 @@ suite('ExtensionsWorkbenchServiceTest', () => {
|
||||
const extension = page.firstPage[0];
|
||||
assert.strictEqual(ExtensionState.Uninstalled, extension.state);
|
||||
|
||||
testObject.install(extension);
|
||||
disposableStore.add(testObject.onChange(target));
|
||||
|
||||
// Installing
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folded-hint {
|
||||
position: absolute;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container .notebook-folded-hint-label {
|
||||
@@ -55,6 +57,22 @@
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container .folded-cell-run-section-button {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
margin-right: 4px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
z-index: var(--z-index-notebook-cell-expand-part-button);
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay > .cell-list-container .folded-cell-run-section-button:hover {
|
||||
background-color: var(--vscode-editorStickyScrollHover-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.monaco-workbench .notebookOverlay .cell-editor-container .monaco-editor .margin-view-overlays .codicon-folding-expanded,
|
||||
.monaco-workbench .notebookOverlay .cell-editor-container .monaco-editor .margin-view-overlays .codicon-folding-collapsed {
|
||||
margin-left: 0;
|
||||
|
||||
@@ -294,7 +294,7 @@ class CellStatusBarItem extends Disposable {
|
||||
this._itemDisposables.clear();
|
||||
|
||||
if (!this._currentItem || this._currentItem.text !== item.text) {
|
||||
new SimpleIconLabel(this.container).text = item.text.replace(/\n/g, ' ');
|
||||
this._itemDisposables.add(new SimpleIconLabel(this.container)).text = item.text.replace(/\n/g, ' ');
|
||||
}
|
||||
|
||||
const resolveColor = (color: ThemeColor | string) => {
|
||||
|
||||
@@ -11,12 +11,21 @@ import { FoldingController } from 'vs/workbench/contrib/notebook/browser/control
|
||||
import { CellEditState, CellFoldingState, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart';
|
||||
import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel';
|
||||
import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange';
|
||||
import { executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class FoldedCellHint extends CellContentPart {
|
||||
|
||||
private readonly _runButtonListener = this._register(new MutableDisposable());
|
||||
private readonly _cellExecutionListener = this._register(new MutableDisposable());
|
||||
|
||||
constructor(
|
||||
private readonly _notebookEditor: INotebookEditor,
|
||||
private readonly _container: HTMLElement,
|
||||
@INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -27,20 +36,27 @@ export class FoldedCellHint extends CellContentPart {
|
||||
|
||||
private update(element: MarkupCellViewModel) {
|
||||
if (!this._notebookEditor.hasModel()) {
|
||||
this._cellExecutionListener.clear();
|
||||
this._runButtonListener.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.isInputCollapsed || element.getEditState() === CellEditState.Editing) {
|
||||
this._cellExecutionListener.clear();
|
||||
this._runButtonListener.clear();
|
||||
DOM.hide(this._container);
|
||||
} else if (element.foldingState === CellFoldingState.Collapsed) {
|
||||
const idx = this._notebookEditor.getViewModel().getCellIndex(element);
|
||||
const length = this._notebookEditor.getViewModel().getFoldedLength(idx);
|
||||
DOM.reset(this._container, this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element));
|
||||
|
||||
DOM.reset(this._container, this.getRunFoldedSectionButton({ start: idx, end: idx + length }), this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element));
|
||||
DOM.show(this._container);
|
||||
|
||||
const foldHintTop = element.layoutInfo.previewHeight;
|
||||
this._container.style.top = `${foldHintTop}px`;
|
||||
} else {
|
||||
this._cellExecutionListener.clear();
|
||||
this._runButtonListener.clear();
|
||||
DOM.hide(this._container);
|
||||
}
|
||||
}
|
||||
@@ -67,6 +83,40 @@ export class FoldedCellHint extends CellContentPart {
|
||||
return expandIcon;
|
||||
}
|
||||
|
||||
private getRunFoldedSectionButton(range: ICellRange): HTMLElement {
|
||||
const runAllContainer = DOM.$('span.folded-cell-run-section-button');
|
||||
const cells = this._notebookEditor.getCellsInRange(range);
|
||||
|
||||
const isRunning = cells.some(cell => {
|
||||
const cellExecution = this._notebookExecutionStateService.getCellExecution(cell.uri);
|
||||
return cellExecution && cellExecution.state === NotebookCellExecutionState.Executing;
|
||||
});
|
||||
|
||||
const runAllIcon = isRunning ?
|
||||
ThemeIcon.modify(executingStateIcon, 'spin') :
|
||||
Codicon.play;
|
||||
runAllContainer.classList.add(...ThemeIcon.asClassNameArray(runAllIcon));
|
||||
|
||||
this._runButtonListener.value = DOM.addDisposableListener(runAllContainer, DOM.EventType.CLICK, () => {
|
||||
this._notebookEditor.executeNotebookCells(cells);
|
||||
});
|
||||
|
||||
this._cellExecutionListener.value = this._notebookExecutionStateService.onDidChangeExecution(() => {
|
||||
const isRunning = cells.some(cell => {
|
||||
const cellExecution = this._notebookExecutionStateService.getCellExecution(cell.uri);
|
||||
return cellExecution && cellExecution.state === NotebookCellExecutionState.Executing;
|
||||
});
|
||||
|
||||
const runAllIcon = isRunning ?
|
||||
ThemeIcon.modify(executingStateIcon, 'spin') :
|
||||
Codicon.play;
|
||||
runAllContainer.className = '';
|
||||
runAllContainer.classList.add('folded-cell-run-section-button', ...ThemeIcon.asClassNameArray(runAllIcon));
|
||||
});
|
||||
|
||||
return runAllContainer;
|
||||
}
|
||||
|
||||
override updateInternalLayoutNow(element: MarkupCellViewModel) {
|
||||
this.update(element);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod
|
||||
import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel';
|
||||
import { CellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl';
|
||||
import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
@@ -109,6 +110,8 @@ abstract class AbstractCellRenderer {
|
||||
export class MarkupCellRenderer extends AbstractCellRenderer implements IListRenderer<MarkupCellViewModel, MarkdownCellRenderTemplate> {
|
||||
static readonly TEMPLATE_ID = 'markdown_cell';
|
||||
|
||||
private _notebookExecutionStateService: INotebookExecutionStateService;
|
||||
|
||||
constructor(
|
||||
notebookEditor: INotebookEditorDelegate,
|
||||
dndController: CellDragAndDropController,
|
||||
@@ -120,8 +123,10 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
||||
@IMenuService menuService: IMenuService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@INotificationService notificationService: INotificationService,
|
||||
@INotebookExecutionStateService notebookExecutionStateService: INotebookExecutionStateService
|
||||
) {
|
||||
super(instantiationService, notebookEditor, contextMenuService, menuService, configurationService, keybindingService, notificationService, contextKeyServiceProvider, 'markdown', dndController);
|
||||
this._notebookExecutionStateService = notebookExecutionStateService;
|
||||
}
|
||||
|
||||
get templateId() {
|
||||
@@ -169,7 +174,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellChatPart, this.notebookEditor, cellChatPart)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellEditorStatusBar, this.notebookEditor, container, editorPart, undefined)),
|
||||
templateDisposables.add(new CellFocusIndicator(this.notebookEditor, titleToolbar, focusIndicatorTop, focusIndicatorLeft, focusIndicatorRight, focusIndicatorBottom)),
|
||||
templateDisposables.add(new FoldedCellHint(this.notebookEditor, DOM.append(container, $('.notebook-folded-hint')))),
|
||||
templateDisposables.add(new FoldedCellHint(this.notebookEditor, DOM.append(container, $('.notebook-folded-hint')), this._notebookExecutionStateService)),
|
||||
templateDisposables.add(new CellDecorations(rootContainer, decorationContainer)),
|
||||
templateDisposables.add(scopedInstaService.createInstance(CellComments, this.notebookEditor, cellCommentPartContainer)),
|
||||
templateDisposables.add(new CollapsedCellInput(this.notebookEditor, cellInputCollapsedContainer)),
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import 'vs/css!./outlinePane';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { TimeoutTimer, timeout } from 'vs/base/common/async';
|
||||
import { IDisposable, toDisposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -304,7 +304,19 @@ export class OutlinePane extends ViewPane implements IOutlinePane {
|
||||
|
||||
// feature: reveal outline selection in editor
|
||||
// on change -> reveal/select defining range
|
||||
this._editorControlDisposables.add(tree.onDidOpen(e => newOutline.reveal(e.element, e.editorOptions, e.sideBySide)));
|
||||
let idPool = 0;
|
||||
this._editorControlDisposables.add(tree.onDidOpen(async e => {
|
||||
const myId = ++idPool;
|
||||
const isDoubleClick = e.browserEvent?.type === 'dblclick';
|
||||
if (!isDoubleClick) {
|
||||
// workaround for https://github.com/microsoft/vscode/issues/206424
|
||||
await timeout(150);
|
||||
if (myId !== idPool) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
await newOutline.reveal(e.element, e.editorOptions, e.sideBySide, isDoubleClick);
|
||||
}));
|
||||
// feature: reveal editor selection in outline
|
||||
const revealActiveElement = () => {
|
||||
if (!this._outlineViewState.followCursor || !newOutline.activeElement) {
|
||||
|
||||
@@ -141,6 +141,7 @@ export class DefineKeybindingWidget extends Widget {
|
||||
private _keybindingInputWidget: KeybindingsSearchWidget;
|
||||
private _outputNode: HTMLElement;
|
||||
private _showExistingKeybindingsNode: HTMLElement;
|
||||
private _keybindingDisposables = this._register(new DisposableStore());
|
||||
|
||||
private _chords: ResolvedKeybinding[] | null = null;
|
||||
private _isVisible: boolean = false;
|
||||
@@ -238,17 +239,18 @@ export class DefineKeybindingWidget extends Widget {
|
||||
}
|
||||
|
||||
private onKeybinding(keybinding: ResolvedKeybinding[] | null): void {
|
||||
this._keybindingDisposables.clear();
|
||||
this._chords = keybinding;
|
||||
dom.clearNode(this._outputNode);
|
||||
dom.clearNode(this._showExistingKeybindingsNode);
|
||||
|
||||
const firstLabel = new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles);
|
||||
const firstLabel = this._keybindingDisposables.add(new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles));
|
||||
firstLabel.set(this._chords?.[0] ?? undefined);
|
||||
|
||||
if (this._chords) {
|
||||
for (let i = 1; i < this._chords.length; i++) {
|
||||
this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to")));
|
||||
const chordLabel = new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles);
|
||||
const chordLabel = this._keybindingDisposables.add(new KeybindingLabel(this._outputNode, OS, defaultKeybindingLabelStyles));
|
||||
chordLabel.set(this._chords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration';
|
||||
import { registerNavigableContainer } from 'vs/workbench/browser/actions/widgetNavigationCommands';
|
||||
import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
@@ -897,6 +899,7 @@ class ActionsColumnRenderer implements ITableRenderer<IKeybindingItemEntry, IAct
|
||||
|
||||
interface ICommandColumnTemplateData {
|
||||
commandColumn: HTMLElement;
|
||||
commandColumnHover: ICustomHover;
|
||||
commandLabelContainer: HTMLElement;
|
||||
commandLabel: HighlightedLabel;
|
||||
commandDefaultLabelContainer: HTMLElement;
|
||||
@@ -913,13 +916,14 @@ class CommandColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ICom
|
||||
|
||||
renderTemplate(container: HTMLElement): ICommandColumnTemplateData {
|
||||
const commandColumn = DOM.append(container, $('.command'));
|
||||
const commandColumnHover = setupCustomHover(getDefaultHoverDelegate('mouse'), commandColumn, '');
|
||||
const commandLabelContainer = DOM.append(commandColumn, $('.command-label'));
|
||||
const commandLabel = new HighlightedLabel(commandLabelContainer);
|
||||
const commandDefaultLabelContainer = DOM.append(commandColumn, $('.command-default-label'));
|
||||
const commandDefaultLabel = new HighlightedLabel(commandDefaultLabelContainer);
|
||||
const commandIdLabelContainer = DOM.append(commandColumn, $('.command-id.code'));
|
||||
const commandIdLabel = new HighlightedLabel(commandIdLabelContainer);
|
||||
return { commandColumn, commandLabelContainer, commandLabel, commandDefaultLabelContainer, commandDefaultLabel, commandIdLabelContainer, commandIdLabel };
|
||||
return { commandColumn, commandColumnHover, commandLabelContainer, commandLabel, commandDefaultLabelContainer, commandDefaultLabel, commandIdLabelContainer, commandIdLabel };
|
||||
}
|
||||
|
||||
renderElement(keybindingItemEntry: IKeybindingItemEntry, index: number, templateData: ICommandColumnTemplateData, height: number | undefined): void {
|
||||
@@ -928,7 +932,9 @@ class CommandColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ICom
|
||||
const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches;
|
||||
|
||||
templateData.commandColumn.classList.toggle('vertical-align-column', commandIdMatched || commandDefaultLabelMatched);
|
||||
templateData.commandColumn.title = keybindingItem.commandLabel ? localize('title', "{0} ({1})", keybindingItem.commandLabel, keybindingItem.command) : keybindingItem.command;
|
||||
const title = keybindingItem.commandLabel ? localize('title', "{0} ({1})", keybindingItem.commandLabel, keybindingItem.command) : keybindingItem.command;
|
||||
templateData.commandColumn.setAttribute('aria-label', title);
|
||||
templateData.commandColumnHover.update(title);
|
||||
|
||||
if (keybindingItem.commandLabel) {
|
||||
templateData.commandLabelContainer.classList.remove('hide');
|
||||
@@ -956,6 +962,7 @@ class CommandColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ICom
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ICommandColumnTemplateData): void {
|
||||
templateData.commandColumnHover.dispose();
|
||||
templateData.commandDefaultLabel.dispose();
|
||||
templateData.commandIdLabel.dispose();
|
||||
templateData.commandLabel.dispose();
|
||||
@@ -989,11 +996,13 @@ class KeybindingColumnRenderer implements ITableRenderer<IKeybindingItemEntry, I
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IKeybindingColumnTemplateData): void {
|
||||
templateData.keybindingLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
interface ISourceColumnTemplateData {
|
||||
sourceColumn: HTMLElement;
|
||||
sourceColumnHover: ICustomHover;
|
||||
sourceLabel: HighlightedLabel;
|
||||
extensionContainer: HTMLElement;
|
||||
extensionLabel: HTMLAnchorElement;
|
||||
@@ -1027,11 +1036,12 @@ class SourceColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ISour
|
||||
|
||||
renderTemplate(container: HTMLElement): ISourceColumnTemplateData {
|
||||
const sourceColumn = DOM.append(container, $('.source'));
|
||||
const sourceColumnHover = setupCustomHover(getDefaultHoverDelegate('mouse'), sourceColumn, '');
|
||||
const sourceLabel = new HighlightedLabel(DOM.append(sourceColumn, $('.source-label')));
|
||||
const extensionContainer = DOM.append(sourceColumn, $('.extension-container'));
|
||||
const extensionLabel = DOM.append<HTMLAnchorElement>(extensionContainer, $('a.extension-label', { tabindex: 0 }));
|
||||
const extensionId = new HighlightedLabel(DOM.append(extensionContainer, $('.extension-id-container.code')));
|
||||
return { sourceColumn, sourceLabel, extensionLabel, extensionContainer, extensionId, disposables: new DisposableStore() };
|
||||
return { sourceColumn, sourceColumnHover, sourceLabel, extensionLabel, extensionContainer, extensionId, disposables: new DisposableStore() };
|
||||
}
|
||||
|
||||
renderElement(keybindingItemEntry: IKeybindingItemEntry, index: number, templateData: ISourceColumnTemplateData, height: number | undefined): void {
|
||||
@@ -1039,14 +1049,14 @@ class SourceColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ISour
|
||||
if (isString(keybindingItemEntry.keybindingItem.source)) {
|
||||
templateData.extensionContainer.classList.add('hide');
|
||||
templateData.sourceLabel.element.classList.remove('hide');
|
||||
templateData.sourceColumn.title = '';
|
||||
templateData.sourceColumnHover.update('');
|
||||
templateData.sourceLabel.set(keybindingItemEntry.keybindingItem.source || '-', keybindingItemEntry.sourceMatches);
|
||||
} else {
|
||||
templateData.extensionContainer.classList.remove('hide');
|
||||
templateData.sourceLabel.element.classList.add('hide');
|
||||
const extension = keybindingItemEntry.keybindingItem.source;
|
||||
const extensionLabel = extension.displayName ?? extension.identifier.value;
|
||||
templateData.sourceColumn.title = localize('extension label', "Extension ({0})", extensionLabel);
|
||||
templateData.sourceColumnHover.update(localize('extension label', "Extension ({0})", extensionLabel));
|
||||
templateData.extensionLabel.textContent = extensionLabel;
|
||||
templateData.disposables.add(onClick(templateData.extensionLabel, () => {
|
||||
this.extensionsWorkbenchService.open(extension.identifier.value);
|
||||
@@ -1062,6 +1072,7 @@ class SourceColumnRenderer implements ITableRenderer<IKeybindingItemEntry, ISour
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ISourceColumnTemplateData): void {
|
||||
templateData.sourceColumnHover.dispose();
|
||||
templateData.disposables.dispose();
|
||||
templateData.sourceLabel.dispose();
|
||||
templateData.extensionId.dispose();
|
||||
@@ -1137,12 +1148,10 @@ class WhenColumnRenderer implements ITableRenderer<IKeybindingItemEntry, IWhenCo
|
||||
) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): IWhenColumnTemplateData {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
const element = DOM.append(container, $('.when'));
|
||||
|
||||
const whenLabelContainer = DOM.append(element, $('div.when-label'));
|
||||
const whenLabel = disposables.add(new HighlightedLabel(whenLabelContainer));
|
||||
const whenLabel = new HighlightedLabel(whenLabelContainer);
|
||||
|
||||
const whenInputContainer = DOM.append(element, $('div.when-input-container'));
|
||||
|
||||
@@ -1151,7 +1160,7 @@ class WhenColumnRenderer implements ITableRenderer<IKeybindingItemEntry, IWhenCo
|
||||
whenLabelContainer,
|
||||
whenLabel,
|
||||
whenInputContainer,
|
||||
disposables,
|
||||
disposables: new DisposableStore(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1193,16 +1202,15 @@ class WhenColumnRenderer implements ITableRenderer<IKeybindingItemEntry, IWhenCo
|
||||
|
||||
if (keybindingItemEntry.keybindingItem.when) {
|
||||
templateData.whenLabel.set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches, keybindingItemEntry.keybindingItem.when);
|
||||
templateData.element.title = keybindingItemEntry.keybindingItem.when;
|
||||
templateData.disposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), templateData.element, keybindingItemEntry.keybindingItem.when));
|
||||
} else {
|
||||
templateData.whenLabel.set('-');
|
||||
templateData.element.title = '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: IWhenColumnTemplateData): void {
|
||||
templateData.disposables.dispose();
|
||||
templateData.whenLabel.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ import { isWorkspaceFolder, IWorkspaceContextService, IWorkspaceFolder, Workbenc
|
||||
import { settingsEditIcon, settingsScopeDropDownIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { ICustomHover, setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
export class FolderSettingsActionViewItem extends BaseActionViewItem {
|
||||
|
||||
private _folder: IWorkspaceFolder | null;
|
||||
@@ -41,6 +43,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem {
|
||||
|
||||
private container!: HTMLElement;
|
||||
private anchorElement!: HTMLElement;
|
||||
private anchorElementHover!: ICustomHover;
|
||||
private labelElement!: HTMLElement;
|
||||
private detailsElement!: HTMLElement;
|
||||
private dropDownElement!: HTMLElement;
|
||||
@@ -87,6 +90,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem {
|
||||
'aria-haspopup': 'true',
|
||||
'tabindex': '0'
|
||||
}, this.labelElement, this.detailsElement, this.dropDownElement);
|
||||
this.anchorElementHover = setupCustomHover(getDefaultHoverDelegate('mouse'), this.anchorElement, '');
|
||||
this._register(DOM.addDisposableListener(this.anchorElement, DOM.EventType.MOUSE_DOWN, e => DOM.EventHelper.stop(e)));
|
||||
this._register(DOM.addDisposableListener(this.anchorElement, DOM.EventType.CLICK, e => this.onClick(e)));
|
||||
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, e => this.onKeyUp(e)));
|
||||
@@ -145,7 +149,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem {
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
if (this._folder) {
|
||||
this.labelElement.textContent = this._folder.name;
|
||||
this.anchorElement.title = this._folder.name;
|
||||
this.anchorElementHover.update(this._folder.name);
|
||||
const detailsText = this.labelWithCount(this._action.label, total);
|
||||
this.detailsElement.textContent = detailsText;
|
||||
this.dropDownElement.classList.toggle('hide', workspace.folders.length === 1 || !this._action.checked);
|
||||
@@ -153,7 +157,7 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem {
|
||||
const labelText = this.labelWithCount(this._action.label, total);
|
||||
this.labelElement.textContent = labelText;
|
||||
this.detailsElement.textContent = '';
|
||||
this.anchorElement.title = this._action.label;
|
||||
this.anchorElementHover.update(this._action.label);
|
||||
this.dropDownElement.classList.remove('hide');
|
||||
}
|
||||
|
||||
|
||||
@@ -135,12 +135,12 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
|
||||
}
|
||||
|
||||
private createWorkspaceTrustIndicator(): SettingIndicator {
|
||||
const disposables = new DisposableStore();
|
||||
const workspaceTrustElement = $('span.setting-indicator.setting-item-workspace-trust');
|
||||
const workspaceTrustLabel = new SimpleIconLabel(workspaceTrustElement);
|
||||
const workspaceTrustLabel = disposables.add(new SimpleIconLabel(workspaceTrustElement));
|
||||
workspaceTrustLabel.text = '$(warning) ' + localize('workspaceUntrustedLabel', "Setting value not applied");
|
||||
|
||||
const content = localize('trustLabel', "The setting value can only be applied in a trusted workspace.");
|
||||
const disposables = new DisposableStore();
|
||||
const showHover = (focus: boolean) => {
|
||||
return this.hoverService.showHover({
|
||||
...this.defaultHoverOptions,
|
||||
@@ -164,23 +164,24 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
|
||||
}
|
||||
|
||||
private createScopeOverridesIndicator(): SettingIndicator {
|
||||
const disposables = new DisposableStore();
|
||||
// Don't add .setting-indicator class here, because it gets conditionally added later.
|
||||
const otherOverridesElement = $('span.setting-item-overrides');
|
||||
const otherOverridesLabel = new SimpleIconLabel(otherOverridesElement);
|
||||
const otherOverridesLabel = disposables.add(new SimpleIconLabel(otherOverridesElement));
|
||||
return {
|
||||
element: otherOverridesElement,
|
||||
label: otherOverridesLabel,
|
||||
disposables: new DisposableStore()
|
||||
disposables
|
||||
};
|
||||
}
|
||||
|
||||
private createSyncIgnoredIndicator(): SettingIndicator {
|
||||
const disposables = new DisposableStore();
|
||||
const syncIgnoredElement = $('span.setting-indicator.setting-item-ignored');
|
||||
const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement);
|
||||
const syncIgnoredLabel = disposables.add(new SimpleIconLabel(syncIgnoredElement));
|
||||
syncIgnoredLabel.text = localize('extensionSyncIgnoredLabel', 'Not synced');
|
||||
|
||||
const syncIgnoredHoverContent = localize('syncIgnoredTitle', "This setting is ignored during sync");
|
||||
const disposables = new DisposableStore();
|
||||
const showHover = (focus: boolean) => {
|
||||
return this.hoverService.showHover({
|
||||
...this.defaultHoverOptions,
|
||||
@@ -193,19 +194,20 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
|
||||
return {
|
||||
element: syncIgnoredElement,
|
||||
label: syncIgnoredLabel,
|
||||
disposables: new DisposableStore()
|
||||
disposables
|
||||
};
|
||||
}
|
||||
|
||||
private createDefaultOverrideIndicator(): SettingIndicator {
|
||||
const disposables = new DisposableStore();
|
||||
const defaultOverrideIndicator = $('span.setting-indicator.setting-item-default-overridden');
|
||||
const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator);
|
||||
const defaultOverrideLabel = disposables.add(new SimpleIconLabel(defaultOverrideIndicator));
|
||||
defaultOverrideLabel.text = localize('defaultOverriddenLabel', "Default value changed");
|
||||
|
||||
return {
|
||||
element: defaultOverrideIndicator,
|
||||
label: defaultOverrideLabel,
|
||||
disposables: new DisposableStore()
|
||||
disposables
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ISetting, ISettingsGroup, SettingValueType } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { getInvalidTypeError } from 'vs/workbench/services/preferences/common/preferencesValidation';
|
||||
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
@@ -796,13 +798,13 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
|
||||
const labelCategoryContainer = DOM.append(titleElement, $('.setting-item-cat-label-container'));
|
||||
const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category'));
|
||||
const labelElementContainer = DOM.append(labelCategoryContainer, $('span.setting-item-label'));
|
||||
const labelElement = new SimpleIconLabel(labelElementContainer);
|
||||
const labelElement = toDispose.add(new SimpleIconLabel(labelElementContainer));
|
||||
const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement);
|
||||
toDispose.add(indicatorsLabel);
|
||||
|
||||
const descriptionElement = DOM.append(container, $('.setting-item-description'));
|
||||
const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator'));
|
||||
modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope.");
|
||||
toDispose.add(setupCustomHover(getDefaultHoverDelegate('mouse'), modifiedIndicatorElement, () => localize('modified', "The setting has been configured in the current scope.")));
|
||||
|
||||
const valueElement = DOM.append(container, $('.setting-item-value'));
|
||||
const controlElement = DOM.append(valueElement, $('div.setting-item-control'));
|
||||
@@ -889,7 +891,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre
|
||||
|
||||
const titleTooltip = setting.key + (element.isConfigured ? ' - Modified' : '');
|
||||
template.categoryElement.textContent = element.displayCategory ? (element.displayCategory + ': ') : '';
|
||||
template.categoryElement.title = titleTooltip;
|
||||
template.elementDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), template.categoryElement, titleTooltip));
|
||||
|
||||
template.labelElement.text = element.displayLabel;
|
||||
template.labelElement.title = titleTooltip;
|
||||
@@ -1817,24 +1819,25 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre
|
||||
_container.classList.add('setting-item');
|
||||
_container.classList.add('setting-item-bool');
|
||||
|
||||
const toDispose = new DisposableStore();
|
||||
|
||||
const container = DOM.append(_container, $(AbstractSettingRenderer.CONTENTS_SELECTOR));
|
||||
container.classList.add('settings-row-inner-container');
|
||||
|
||||
const titleElement = DOM.append(container, $('.setting-item-title'));
|
||||
const categoryElement = DOM.append(titleElement, $('span.setting-item-category'));
|
||||
const labelElementContainer = DOM.append(titleElement, $('span.setting-item-label'));
|
||||
const labelElement = new SimpleIconLabel(labelElementContainer);
|
||||
const labelElement = toDispose.add(new SimpleIconLabel(labelElementContainer));
|
||||
const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement);
|
||||
|
||||
const descriptionAndValueElement = DOM.append(container, $('.setting-item-value-description'));
|
||||
const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control'));
|
||||
const descriptionElement = DOM.append(descriptionAndValueElement, $('.setting-item-description'));
|
||||
const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator'));
|
||||
modifiedIndicatorElement.title = localize('modified', "The setting has been configured in the current scope.");
|
||||
toDispose.add(setupCustomHover(getDefaultHoverDelegate('mouse'), modifiedIndicatorElement, localize('modified', "The setting has been configured in the current scope.")));
|
||||
|
||||
const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message'));
|
||||
|
||||
const toDispose = new DisposableStore();
|
||||
const checkbox = new Toggle({ icon: Codicon.check, actionClassName: 'setting-value-checkbox', isChecked: true, title: '', ...unthemedToggleStyles });
|
||||
controlElement.appendChild(checkbox.domNode);
|
||||
toDispose.add(checkbox);
|
||||
|
||||
@@ -27,6 +27,8 @@ import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { settingsDiscardIcon, settingsEditIcon, settingsRemoveIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons';
|
||||
import { settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry';
|
||||
import { defaultButtonStyles, getInputBoxStyle, getSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles';
|
||||
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
|
||||
const $ = DOM.$;
|
||||
|
||||
@@ -673,8 +675,8 @@ export class ListSettingWidget extends AbstractListSettingWidget<IListDataItem>
|
||||
: localize('listSiblingHintLabel', "List item `{0}` with sibling `${1}`", value.data, sibling);
|
||||
|
||||
const { rowElement } = rowElementGroup;
|
||||
rowElement.title = title;
|
||||
rowElement.setAttribute('aria-label', rowElement.title);
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), rowElement, title));
|
||||
rowElement.setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
protected getLocalizedStrings() {
|
||||
@@ -733,8 +735,8 @@ export class ExcludeSettingWidget extends ListSettingWidget {
|
||||
: localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", value.data, sibling);
|
||||
|
||||
const { rowElement } = rowElementGroup;
|
||||
rowElement.title = title;
|
||||
rowElement.setAttribute('aria-label', rowElement.title);
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), rowElement, title));
|
||||
rowElement.setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
protected override getLocalizedStrings() {
|
||||
@@ -763,8 +765,8 @@ export class IncludeSettingWidget extends ListSettingWidget {
|
||||
: localize('includeSiblingHintLabel', "Include files matching `{0}`, only when a file matching `{1}` is present", value.data, sibling);
|
||||
|
||||
const { rowElement } = rowElementGroup;
|
||||
rowElement.title = title;
|
||||
rowElement.setAttribute('aria-label', rowElement.title);
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), rowElement, title));
|
||||
rowElement.setAttribute('aria-label', title);
|
||||
}
|
||||
|
||||
protected override getLocalizedStrings() {
|
||||
@@ -1161,10 +1163,10 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget<IObje
|
||||
const accessibleDescription = localize('objectPairHintLabel', "The property `{0}` is set to `{1}`.", item.key.data, item.value.data);
|
||||
|
||||
const keyDescription = this.getEnumDescription(item.key) ?? item.keyDescription ?? accessibleDescription;
|
||||
keyElement.title = keyDescription;
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), keyElement, keyDescription));
|
||||
|
||||
const valueDescription = this.getEnumDescription(item.value) ?? accessibleDescription;
|
||||
valueElement!.title = valueDescription;
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), valueElement!, valueDescription));
|
||||
|
||||
rowElement.setAttribute('aria-label', accessibleDescription);
|
||||
}
|
||||
@@ -1315,7 +1317,7 @@ export class ObjectSettingCheckboxWidget extends AbstractListSettingWidget<IObje
|
||||
const title = item.keyDescription ?? accessibleDescription;
|
||||
const { rowElement, keyElement, valueElement } = rowElementGroup;
|
||||
|
||||
keyElement.title = title;
|
||||
this.listDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), keyElement, title));
|
||||
valueElement!.setAttribute('aria-label', accessibleDescription);
|
||||
rowElement.setAttribute('aria-label', accessibleDescription);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory';
|
||||
import { setupCustomHover } from 'vs/base/browser/ui/hover/updatableHoverWidget';
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { RenderIndentGuides } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -102,6 +105,7 @@ const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry';
|
||||
interface ITOCEntryTemplate {
|
||||
labelElement: HTMLElement;
|
||||
countElement: HTMLElement;
|
||||
elementDisposables: DisposableStore;
|
||||
}
|
||||
|
||||
export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, ITOCEntryTemplate> {
|
||||
@@ -111,17 +115,20 @@ export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, neve
|
||||
renderTemplate(container: HTMLElement): ITOCEntryTemplate {
|
||||
return {
|
||||
labelElement: DOM.append(container, $('.settings-toc-entry')),
|
||||
countElement: DOM.append(container, $('.settings-toc-count'))
|
||||
countElement: DOM.append(container, $('.settings-toc-count')),
|
||||
elementDisposables: new DisposableStore()
|
||||
};
|
||||
}
|
||||
|
||||
renderElement(node: ITreeNode<SettingsTreeGroupElement>, index: number, template: ITOCEntryTemplate): void {
|
||||
template.elementDisposables.clear();
|
||||
|
||||
const element = node.element;
|
||||
const count = element.count;
|
||||
const label = element.label;
|
||||
|
||||
template.labelElement.textContent = label;
|
||||
template.labelElement.title = label;
|
||||
template.elementDisposables.add(setupCustomHover(getDefaultHoverDelegate('mouse'), template.labelElement, label));
|
||||
|
||||
if (count) {
|
||||
template.countElement.textContent = ` (${count})`;
|
||||
@@ -131,6 +138,7 @@ export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, neve
|
||||
}
|
||||
|
||||
disposeTemplate(templateData: ITOCEntryTemplate): void {
|
||||
templateData.elementDisposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -441,7 +441,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
|
||||
}, 500, false, true)(async (markerEvent) => {
|
||||
markerChanged?.dispose();
|
||||
markerChanged = undefined;
|
||||
if (!markerEvent.includes(modelEvent.uri) || (this.markerService.read({ resource: modelEvent.uri }).length !== 0)) {
|
||||
if (!markerEvent || !markerEvent.includes(modelEvent.uri) || (this.markerService.read({ resource: modelEvent.uri }).length !== 0)) {
|
||||
return;
|
||||
}
|
||||
const oldLines = Array.from(this.lines);
|
||||
|
||||
@@ -154,6 +154,16 @@ export class TerminalLinkQuickpick extends DisposableStore {
|
||||
r();
|
||||
}));
|
||||
disposables.add(Event.once(pick.onDidAccept)(() => {
|
||||
// Restore terminal scroll state
|
||||
if (this._terminalScrollStateSaved) {
|
||||
const markTracker = this._instance?.xterm?.markTracker;
|
||||
if (markTracker) {
|
||||
markTracker.restoreScrollState();
|
||||
markTracker.clear();
|
||||
this._terminalScrollStateSaved = false;
|
||||
}
|
||||
}
|
||||
|
||||
accepted = true;
|
||||
const event = new TerminalLinkQuickPickEvent(EventType.CLICK);
|
||||
const activeItem = pick.activeItems?.[0];
|
||||
|
||||
+31
-9
@@ -8,7 +8,7 @@ import type { IBufferLine, IMarker, ITerminalOptions, ITheme, Terminal as RawXte
|
||||
import { importAMDNodeModule } from 'vs/amdX';
|
||||
import { $, addDisposableListener, addStandardDisposableListener, getWindow } from 'vs/base/browser/dom';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { debounce, memoize, throttle } from 'vs/base/common/decorators';
|
||||
import { memoize, throttle } from 'vs/base/common/decorators';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { removeAnsiEscapeCodes } from 'vs/base/common/strings';
|
||||
@@ -193,17 +193,18 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
if (command && this._currentStickyCommand !== command) {
|
||||
this._throttledRefresh();
|
||||
} else {
|
||||
this._debouncedRefresh();
|
||||
// If it's the same command, do not throttle as the sticky scroll overlay height may
|
||||
// need to be adjusted. This would cause a flicker if throttled.
|
||||
this._refreshNow();
|
||||
}
|
||||
}
|
||||
|
||||
@debounce(20)
|
||||
private _debouncedRefresh(): void {
|
||||
this._throttledRefresh();
|
||||
}
|
||||
|
||||
@throttle(0)
|
||||
private _throttledRefresh(): void {
|
||||
this._refreshNow();
|
||||
}
|
||||
|
||||
private _refreshNow(): void {
|
||||
const command = this._commandDetection.getCommandForLine(this._xterm.raw.buffer.active.viewportY);
|
||||
|
||||
// The command from viewportY + 1 is used because this one will not be obscured by sticky
|
||||
@@ -245,6 +246,12 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide sticky scroll if the prompt has been trimmed from the buffer
|
||||
if (command.promptStartMarker?.line === -1) {
|
||||
this._setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine sticky scroll line count
|
||||
const buffer = xterm.buffer.active;
|
||||
const promptRowCount = command.getPromptRowCount();
|
||||
@@ -281,7 +288,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear attrs, reset cursor position, clear right
|
||||
// Get the line content of the command from the terminal
|
||||
const content = this._serializeAddon.serialize({
|
||||
range: {
|
||||
start: stickyScrollLineStart + rowOffset,
|
||||
@@ -299,6 +306,7 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
// Write content if it differs
|
||||
if (content && this._currentContent !== content) {
|
||||
this._stickyScrollOverlay.resize(this._stickyScrollOverlay.cols, stickyScrollLineCount);
|
||||
// Clear attrs, reset cursor position, clear right
|
||||
this._stickyScrollOverlay.write('\x1b[0m\x1b[H\x1b[2J');
|
||||
this._stickyScrollOverlay.write(content);
|
||||
this._currentContent = content;
|
||||
@@ -317,7 +325,18 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
const termBox = xterm.element.getBoundingClientRect();
|
||||
const rowHeight = termBox.height / xterm.rows;
|
||||
const overlayHeight = stickyScrollLineCount * rowHeight;
|
||||
this._element.style.bottom = `${termBox.height - overlayHeight + 1}px`;
|
||||
|
||||
// Adjust sticky scroll content if it would below the end of the command, obscuring the
|
||||
// following command.
|
||||
let endMarkerOffset = 0;
|
||||
if (!isPartialCommand && command.endMarker && command.endMarker.line !== -1) {
|
||||
if (buffer.viewportY + stickyScrollLineCount > command.endMarker.line) {
|
||||
const diff = buffer.viewportY + stickyScrollLineCount - command.endMarker.line;
|
||||
endMarkerOffset = diff * rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
this._element.style.bottom = `${termBox.height - overlayHeight + 1 + endMarkerOffset}px`;
|
||||
}
|
||||
} else {
|
||||
this._setVisible(false);
|
||||
@@ -376,6 +395,9 @@ export class TerminalStickyScrollOverlay extends Disposable {
|
||||
}
|
||||
}));
|
||||
|
||||
// Forward mouse events to the terminal
|
||||
this._register(addStandardDisposableListener(hoverOverlay, 'wheel', e => this._xterm?.raw.element?.dispatchEvent(new WheelEvent(e.type, e))));
|
||||
|
||||
// Context menu - stop propagation on mousedown because rightClickBehavior listens on
|
||||
// mousedown, not contextmenu
|
||||
this._register(addDisposableListener(hoverOverlay, 'mousedown', e => {
|
||||
|
||||
@@ -21,6 +21,7 @@ export const allApiProposals = Object.freeze({
|
||||
codeActionRanges: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codeActionRanges.d.ts',
|
||||
codiconDecoration: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.codiconDecoration.d.ts',
|
||||
commentReactor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentReactor.d.ts',
|
||||
commentingRangeHint: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentingRangeHint.d.ts',
|
||||
commentsDraftState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.commentsDraftState.d.ts',
|
||||
contribCommentEditorActionsMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentEditorActionsMenu.d.ts',
|
||||
contribCommentPeekContext: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribCommentPeekContext.d.ts',
|
||||
|
||||
@@ -83,7 +83,7 @@ export interface IOutline<E> {
|
||||
readonly activeElement: E | undefined;
|
||||
readonly onDidChange: Event<OutlineChangeEvent>;
|
||||
|
||||
reveal(entry: E, options: IEditorOptions, sideBySide: boolean): Promise<void> | void;
|
||||
reveal(entry: E, options: IEditorOptions, sideBySide: boolean, select: boolean): Promise<void> | void;
|
||||
preview(entry: E): IDisposable;
|
||||
captureViewState(): IDisposable;
|
||||
dispose(): void;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'vscode' {
|
||||
|
||||
// @alexr00 https://github.com/microsoft/vscode/issues/185551
|
||||
|
||||
/**
|
||||
* Commenting range provider for a {@link CommentController comment controller}.
|
||||
*/
|
||||
export interface CommentingRangeProvider {
|
||||
readonly resourceHints?: { schemes: readonly string[] };
|
||||
}
|
||||
}
|
||||
@@ -22,13 +22,13 @@ export class KeybindingsEditor {
|
||||
await this.code.waitForSetValue(SEARCH_INPUT, `@command:${command}`);
|
||||
|
||||
const commandTitle = commandName ? `${commandName} (${command})` : command;
|
||||
await this.code.waitAndClick(`.keybindings-table-container .monaco-list-row .command[title="${commandTitle}"]`);
|
||||
await this.code.waitForElement(`.keybindings-table-container .monaco-list-row.focused.selected .command[title="${commandTitle}"]`);
|
||||
await this.code.waitAndClick(`.keybindings-table-container .monaco-list-row .command[aria-label="${commandTitle}"]`);
|
||||
await this.code.waitForElement(`.keybindings-table-container .monaco-list-row.focused.selected .command[aria-label="${commandTitle}"]`);
|
||||
await this.code.dispatchKeybinding('enter');
|
||||
|
||||
await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input');
|
||||
await this.code.dispatchKeybinding(keybinding);
|
||||
await this.code.dispatchKeybinding('enter');
|
||||
await this.code.waitForElement(`.keybindings-table-container .keybinding-label div[title="${keybindingTitle}"]`);
|
||||
await this.code.waitForElement(`.keybindings-table-container .keybinding-label div[aria-label="${keybindingTitle}"]`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user