mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 01:25:37 -05:00
save richTextCursorPosition/markdownCursorPosition (#17180)
* save richTextCursorPosition/markdownCursorPosition * rename variables * check to see if the nodes exist * added comments * add comments * check if valid offset * pr comments * sqllint error fix
This commit is contained in:
@@ -282,6 +282,10 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
||||
DOM.getContentHeight(this.codeElement.nativeElement)));
|
||||
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
||||
this.horizontalScrollbar();
|
||||
// Move cursor to the last known location
|
||||
if (this.cellModel.markdownCursorPosition) {
|
||||
this._editor.getControl().setPosition(this.cellModel.markdownCursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IMarkdownRenderResult } from 'vs/editor/browser/core/markdownRenderer';
|
||||
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { CellEditModes, ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ICaretPosition, CellEditModes, ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/workbench/services/notebook/browser/outputs/sanitizer';
|
||||
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
|
||||
@@ -211,10 +211,48 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
this._changeRef.detectChanges();
|
||||
}));
|
||||
this._register(this.cellModel.onCellPreviewModeChanged(preview => {
|
||||
// On preview mode change, get the cursor position (get the position only when the selection node is a text node)
|
||||
if (window.getSelection() && window.getSelection().focusNode?.nodeName === '#text' && window.getSelection().getRangeAt(0)) {
|
||||
let selection = window.getSelection().getRangeAt(0);
|
||||
// Check to see if the last cursor position is still the same and skip
|
||||
if (selection.startOffset !== this.cellModel.richTextCursorPosition?.startOffset) {
|
||||
// window.getSelection gives the exact html element and offsets of cursor location
|
||||
// Since we only have the output element reference which is the parent of all html nodes
|
||||
// we iterate through it's child nodes until we get the selection element and store the node indexes
|
||||
// in the startElementNodes and endElementNodes and their offsets respectively.
|
||||
let startElementNodes = [];
|
||||
let startNode = selection.startContainer;
|
||||
let endNode = selection.endContainer;
|
||||
while (startNode !== this.output.nativeElement) {
|
||||
startElementNodes.push(this.getNodeIndex(startNode));
|
||||
startNode = startNode.parentNode;
|
||||
}
|
||||
let endElementNodes = [];
|
||||
while (endNode !== this.output.nativeElement) {
|
||||
endElementNodes.push(this.getNodeIndex(endNode));
|
||||
endNode = endNode.parentNode;
|
||||
}
|
||||
// Create cursor position
|
||||
let cursorPosition: ICaretPosition = {
|
||||
startElementNodes: startElementNodes,
|
||||
startOffset: selection.startOffset,
|
||||
endElementNodes: endElementNodes,
|
||||
endOffset: selection.endOffset
|
||||
};
|
||||
this.cellModel.richTextCursorPosition = cursorPosition;
|
||||
}
|
||||
}
|
||||
this.previewMode = preview;
|
||||
this.focusIfPreviewMode();
|
||||
}));
|
||||
this._register(this.cellModel.onCellMarkdownModeChanged(markdown => {
|
||||
if (!markdown) {
|
||||
let editorControl = this.cellEditors.length > 0 ? this.cellEditors[0].getEditor().getControl() : undefined;
|
||||
if (editorControl) {
|
||||
let selection = editorControl.getSelection();
|
||||
this.cellModel.markdownCursorPosition = selection?.getPosition();
|
||||
}
|
||||
}
|
||||
this.markdownMode = markdown;
|
||||
this.focusIfPreviewMode();
|
||||
}));
|
||||
@@ -236,6 +274,17 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
getNodeIndex(n) {
|
||||
let i = 0;
|
||||
// walk up the node to the top and get it's index
|
||||
n = n.previousSibling;
|
||||
while (n) {
|
||||
i++;
|
||||
n = n.previousSibling;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public cellGuid(): string {
|
||||
return this.cellModel.cellGuid;
|
||||
}
|
||||
@@ -452,6 +501,35 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
} else {
|
||||
this.setSplitViewHeight();
|
||||
}
|
||||
// Move cursor to the richTextCursorPosition
|
||||
// We iterate through the output element childnodes to get to the element of cursor location
|
||||
// If the elements exist, we set the selection, else the cursor defaults to beginning.
|
||||
if (!this.markdownMode && this.cellModel.richTextCursorPosition) {
|
||||
let selection = window.getSelection();
|
||||
let htmlNodes = this.cellModel.richTextCursorPosition.startElementNodes;
|
||||
let depthToNode = htmlNodes.length;
|
||||
let startNodeElement: any = this.output.nativeElement;
|
||||
while (depthToNode-- && startNodeElement) {
|
||||
startNodeElement = startNodeElement.childNodes[htmlNodes[depthToNode]];
|
||||
}
|
||||
htmlNodes = this.cellModel.richTextCursorPosition.endElementNodes;
|
||||
depthToNode = htmlNodes.length;
|
||||
let endNodeElement: any = this.output.nativeElement;
|
||||
while (depthToNode-- && endNodeElement) {
|
||||
endNodeElement = endNodeElement?.childNodes[htmlNodes[depthToNode]];
|
||||
}
|
||||
// check to see if the nodes exist and set the cursor
|
||||
if (startNodeElement && endNodeElement) {
|
||||
// check the offset is still valid (element's text updates can make it invalid)
|
||||
if (startNodeElement.length >= this.cellModel.richTextCursorPosition.startOffset && endNodeElement.length >= this.cellModel.richTextCursorPosition.endOffset) {
|
||||
let range = document.createRange();
|
||||
range.setStart(startNodeElement, this.cellModel.richTextCursorPosition.startOffset);
|
||||
range.setEnd(endNodeElement, this.cellModel.richTextCursorPosition.endOffset);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.setMarkdownEditorHeight(this._markdownMaxHeight);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { localize } from 'vs/nls';
|
||||
import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
import { CellTypes, CellType, NotebookChangeType, TextCellEditModes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ICellModel, IOutputChangedEvent, CellExecutionState, ICellModelOptions, ITableUpdatedEvent, CellEditModes } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel, IOutputChangedEvent, CellExecutionState, ICellModelOptions, ITableUpdatedEvent, CellEditModes, ICaretPosition } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
@@ -31,6 +31,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IInsightOptions } from 'sql/workbench/common/editor/query/chartState';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
|
||||
let modelId = 0;
|
||||
const ads_execute_command = 'ads_execute_command';
|
||||
@@ -85,6 +86,8 @@ export class CellModel extends Disposable implements ICellModel {
|
||||
private _outputCounter = 0; // When re-executing the same cell, ensure that we apply chart options in the same order
|
||||
private _attachments: nb.ICellAttachments | undefined;
|
||||
private _preventNextChartCache: boolean = false;
|
||||
public richTextCursorPosition: ICaretPosition | undefined;
|
||||
public markdownCursorPosition: IPosition | undefined;
|
||||
|
||||
constructor(cellData: nb.ICellContents,
|
||||
private _options: ICellModelOptions,
|
||||
|
||||
@@ -23,6 +23,7 @@ import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvent
|
||||
import type { FutureInternal } from 'sql/workbench/services/notebook/browser/interfaces';
|
||||
import { ICellValue, ResultSetSummary } from 'sql/workbench/services/query/common/query';
|
||||
import { QueryResultId } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
|
||||
export enum ViewMode {
|
||||
Notebook,
|
||||
@@ -548,6 +549,15 @@ export interface ICellModel {
|
||||
* Returns the name of the attachment added to metadata.
|
||||
*/
|
||||
addAttachment(mimeType: string, base64Encoding: string, name: string): string;
|
||||
richTextCursorPosition: ICaretPosition;
|
||||
markdownCursorPosition: IPosition;
|
||||
}
|
||||
|
||||
export interface ICaretPosition {
|
||||
startElementNodes: number[];
|
||||
startOffset: number;
|
||||
endElementNodes: number[];
|
||||
endOffset: number;
|
||||
}
|
||||
|
||||
export interface IModelFactory {
|
||||
|
||||
Reference in New Issue
Block a user