Books/search within notebook (#8426)

* initial commit

* get notebook content

* skeleton for find in notebookModel

* add search function and keyboard shortcut

* add command for hiding find widget

* started on search logic

* continue search logic

* continue search logic

* add findcountchange listener

* notebook find position

* added css class

* hide find widget

* focus find input

* search for multiple occurrences in one line

* start notebook find decorations

* start adding decorations to notebook model

* added editor_model_defaults

* added cursor position

* merged master and resolved husky erros

* initial changes added to Lucyls base implementation

* pass NotebbokRange instead of Range to decorations

* changes after merging master

* temp changes for testing

* style updates from vscode merge

* implemented the empty methods and added supporting functionality from textModel

* just a little error checking

* It gets more and more yellow

* making highlight work between code cells

* highlight only word

* remove highlight on close and maintain the position

* cleanup of unused references

* clean up

* find between code cells refactored

* highlight markdown line and scroll to it

* find index fix

* find index fix

* code clean up

* remove commented code

* tslint fix for: Cannot use global 'NodeJS'

* linting rule fixes

* deltaDecoration base implementation on the base class

* moced class defnitions from interface fikle

* updated action names

* DOM.addClass instead of overwriting

* resooved conflicts

* moved 'find' code away from notebookmodel to sep class

* moved find realted code to seperate folder

* created notebookFindModel

* clean up

* highlight color changes

* spacing and typo fixes

* highlight correct element for nested elements

* do not iterate through paragraphs and li

* find accross notebooks

* keep track of index

* clear decorations on close

* floating promises

* maintain search context

Co-authored-by: Lucy Zhang <lucyzhang929@gmail.com>
Co-authored-by: Chris LaFreniere <40371649+chlafreniere@users.noreply.github.com>
This commit is contained in:
Maddy
2019-12-19 17:21:03 -08:00
committed by GitHub
parent 102d820935
commit 778a34a9d2
22 changed files with 2236 additions and 41 deletions

View File

@@ -6,7 +6,6 @@ import 'vs/css!./code';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventEmitter, OnChanges, SimpleChange, forwardRef, ChangeDetectorRef } from '@angular/core';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
import { ICellModel, notebookConstants, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
@@ -33,6 +32,7 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con
import { ILogService } from 'vs/platform/log/common/log';
import { CollapseComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/collapse.component';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
import { UntitledTextEditorInput } from 'vs/workbench/common/editor/untitledTextEditorInput';
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
@@ -44,7 +44,7 @@ const DEFAULT_OR_LOCAL_CONTEXT_ID = '-1';
selector: CODE_SELECTOR,
templateUrl: decodeURI(require.toUrl('./code.component.html'))
})
export class CodeComponent extends AngularDisposable implements OnInit, OnChanges {
export class CodeComponent extends CellView implements OnInit, OnChanges {
@ViewChild('toolbar', { read: ElementRef }) private toolbarElement: ElementRef;
@ViewChild('moreactions', { read: ElementRef }) private moreActionsElementRef: ElementRef;
@ViewChild('editor', { read: ElementRef }) private codeElement: ElementRef;
@@ -141,6 +141,18 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
}
}
public getEditor(): QueryTextEditor {
return this._editor;
}
public hasEditor(): boolean {
return true;
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
private updateConnectionState(shouldConnect: boolean) {
if (this.isSqlCodeCell()) {
let cellUri = this.cellModel.cellUri.toString();
@@ -173,7 +185,7 @@ export class CodeComponent extends AngularDisposable implements OnInit, OnChange
if (this.destroyed) {
return;
}
this.createEditor();
this.createEditor().catch(e => this.logService.error(e));
this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, e => {
this._layoutEmitter.fire();
}));

View File

@@ -66,6 +66,14 @@ code-component .monaco-editor .decorationsOverviewRuler {
visibility: hidden;
}
.vs code-component .monaco-editor .rangeHighlight {
background-color: rgba(255, 255, 0, 0.2)
}
.vs-dark code-component .monaco-editor .rangeHighlight {
background-color: rgba(255, 255, 0, 0.2)
}
code-component .carbon-taskbar .codicon {
background-size: 20px;
width: 40px;

View File

@@ -4,11 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { nb } from 'azdata';
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, HostListener } from '@angular/core';
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, HostListener, ViewChildren, QueryList } from '@angular/core';
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
import { Deferred } from 'sql/base/common/promise';
import { ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
import { OutputComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/output.component';
export const CODE_SELECTOR: string = 'code-cell-component';
@@ -19,6 +22,8 @@ export const CODE_SELECTOR: string = 'code-cell-component';
})
export class CodeCellComponent extends CellView implements OnInit, OnChanges {
@ViewChildren(CodeComponent) private codeCells: QueryList<ICellEditorProvider>;
@ViewChildren(OutputComponent) private outputCells: QueryList<ICellEditorProvider>;
@Input() cellModel: ICellModel;
@Input() set model(value: NotebookModel) {
this._model = value;
@@ -69,6 +74,17 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
}
}
public get cellEditors(): ICellEditorProvider[] {
let editors: ICellEditorProvider[] = [];
if (this.codeCells) {
editors.push(...this.codeCells.toArray());
}
if (this.outputCells) {
editors.push(...this.outputCells.toArray());
}
return editors;
}
get model(): NotebookModel {
return this._model;
}
@@ -110,4 +126,8 @@ export class CodeCellComponent extends CellView implements OnInit, OnChanges {
get isStdInVisible(): boolean {
return this.cellModel.stdInVisible;
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
}

View File

@@ -64,6 +64,10 @@ export class CollapseComponent extends CellView implements OnInit, OnChanges {
this.cellModel.isCollapsed = !this.cellModel.isCollapsed;
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
public layout() {
}

View File

@@ -5,11 +5,28 @@
import { OnDestroy } from '@angular/core';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
export abstract class CellView extends AngularDisposable implements OnDestroy {
export abstract class CellView extends AngularDisposable implements OnDestroy, ICellEditorProvider {
constructor() {
super();
}
public abstract layout(): void;
public getEditor(): BaseTextEditor | undefined {
return undefined;
}
public hasEditor(): boolean {
return false;
}
public abstract cellGuid(): string;
public deltaDecorations(newDecorationRange: NotebookRange, oldDecorationRange: NotebookRange): void {
}
}

View File

@@ -6,7 +6,6 @@ import 'vs/css!./code';
import 'vs/css!./media/output';
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, SimpleChange, AfterViewInit, forwardRef, ChangeDetectorRef, ComponentRef, ComponentFactoryResolver } from '@angular/core';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { Event } from 'vs/base/common/event';
import { nb } from 'azdata';
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
@@ -21,6 +20,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { localize } from 'vs/nls';
import * as types from 'vs/base/common/types';
import { getErrorMessage } from 'vs/base/common/errors';
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
export const OUTPUT_SELECTOR: string = 'output-component';
const USER_SELECT_CLASS = 'actionselect';
@@ -31,7 +31,7 @@ const componentRegistry = <IMimeComponentRegistry>Registry.as(Extensions.MimeCom
selector: OUTPUT_SELECTOR,
templateUrl: decodeURI(require.toUrl('./output.component.html'))
})
export class OutputComponent extends AngularDisposable implements OnInit, AfterViewInit {
export class OutputComponent extends CellView implements OnInit, AfterViewInit {
@ViewChild('output', { read: ElementRef }) private outputElement: ElementRef;
@ViewChild(ComponentHostDirective) componentHost: ComponentHostDirective;
@Input() cellOutput: nb.ICellOutput;
@@ -184,4 +184,8 @@ export class OutputComponent extends AngularDisposable implements OnInit, AfterV
return;
}
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
}

View File

@@ -82,4 +82,8 @@ export class PlaceholderCellComponent extends CellView implements OnInit, OnChan
public layout() {
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
}

View File

@@ -24,6 +24,7 @@ import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelI
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
import { ISanitizer, defaultSanitizer } from 'sql/workbench/contrib/notebook/browser/outputs/sanitizer';
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
export const TEXT_SELECTOR: string = 'text-cell-component';
const USER_SELECT_CLASS = 'actionselect';
@@ -139,6 +140,10 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
}
}
public cellGuid(): string {
return this.cellModel.cellGuid;
}
public get isTrusted(): boolean {
return this.model.trustedMode;
}
@@ -243,4 +248,66 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
protected toggleMoreActionsButton(isActiveOrHovered: boolean) {
this._cellToggleMoreActions.toggleVisible(!isActiveOrHovered);
}
public deltaDecorations(newDecorationRange: NotebookRange, oldDecorationRange: NotebookRange): void {
if (oldDecorationRange) {
this.removeDecoration(oldDecorationRange);
}
if (newDecorationRange) {
this.addDecoration(newDecorationRange);
}
}
private addDecoration(range: NotebookRange): void {
if (range && this.output && this.output.nativeElement) {
let children = this.getHtmlElements();
let ele = children[range.startLineNumber - 1];
if (ele) {
DOM.addClass(ele, 'rangeHighlight');
ele.scrollIntoView({ behavior: 'smooth' });
}
}
}
private removeDecoration(range: NotebookRange): void {
if (range && this.output && this.output.nativeElement) {
let children = this.getHtmlElements();
let ele = children[range.startLineNumber - 1];
if (ele) {
DOM.removeClass(ele, 'rangeHighlight');
}
}
}
private getHtmlElements(): any[] {
let hostElem = this.output.nativeElement;
let children = [];
for (let element of hostElem.children) {
if (element.nodeName.toLowerCase() === 'table') {
// add table header and table rows.
children.push(element.children[0]);
for (let trow of element.children[1].children) {
children.push(trow);
}
} else if (element.children.length > 1) {
children = children.concat(this.getChildren(element));
} else {
children.push(element);
}
}
return children;
}
private getChildren(parent: any): any[] {
let children: any = [];
if (parent.children.length > 1 && parent.nodeName.toLowerCase() !== 'li' && parent.nodeName.toLowerCase() !== 'p') {
for (let child of parent.children) {
children = children.concat(this.getChildren(child));
}
} else {
return parent;
}
return children;
}
}

View File

@@ -17,6 +17,14 @@ text-cell-component .notebook-preview {
user-select: text;
}
.vs .notebook-preview .rangeHighlight {
background-color: rgba(255, 255, 0, 0.2)
}
.vs-dark .notebook-preview .rangeHighlight {
background-color: rgba(255, 255, 0, 0.2)
}
text-cell-component table {
border-collapse: collapse;
border-spacing: 0;
@@ -47,4 +55,4 @@ text-cell-component tr {
text-cell-component th {
font-weight: bold;
}
}