mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 17:22:59 -05:00
* Fix #5238 Notebooks should support relative links - Added detection of relative #links inside notebooks - Added handling of these, at least for current notebook Not handled: open other notebook & scroll to position.
This commit is contained in:
@@ -5,19 +5,27 @@
|
||||
|
||||
import { Directive, Inject, HostListener, Input } from '@angular/core';
|
||||
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
|
||||
const knownSchemes = new Set(['http', 'https', 'file', 'mailto', 'data', `${product.urlProtocol}`, 'azuredatastudio', 'azuredatastudio-insiders', 'vscode', 'vscode-insiders', 'vscode-resource']);
|
||||
@Directive({
|
||||
selector: '[link-handler]',
|
||||
})
|
||||
export class LinkHandlerDirective {
|
||||
|
||||
private workbenchFilePath: URI;
|
||||
@Input() isTrusted: boolean;
|
||||
constructor(@Inject(IOpenerService) private readonly openerService: IOpenerService) {
|
||||
@Input() notebookUri: URI;
|
||||
|
||||
constructor(
|
||||
@Inject(IOpenerService) private readonly openerService: IOpenerService,
|
||||
@Inject(INotebookService) private readonly notebookService: INotebookService
|
||||
) {
|
||||
this.workbenchFilePath = URI.parse(require.toUrl('vs/code/electron-browser/workbench/workbench.html'));
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
@@ -52,7 +60,11 @@ export class LinkHandlerDirective {
|
||||
// ignore
|
||||
}
|
||||
if (uri && this.openerService && this.isSupportedLink(uri)) {
|
||||
this.openerService.open(uri).catch(onUnexpectedError);
|
||||
if (uri.fragment && uri.fragment.length > 0 && uri.path === this.workbenchFilePath.path) {
|
||||
this.notebookService.navigateTo(this.notebookUri, uri.fragment);
|
||||
} else {
|
||||
this.openerService.open(uri).catch(onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: column">
|
||||
<div #outputarea link-handler [isTrusted]="isTrusted" class="notebook-output" style="flex: 0 0 auto;">
|
||||
<div #outputarea link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-output" style="flex: 0 0 auto;">
|
||||
<output-component *ngFor="let output of cellModel.outputs" [cellOutput]="output" [trustedMode] = "cellModel.trustedMode" [cellModel]="cellModel" [activeCellId]="activeCellId">
|
||||
</output-component>
|
||||
</div>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||
import { ICellModel } from 'sql/workbench/parts/notebook/models/modelInterfaces';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const OUTPUT_AREA_SELECTOR: string = 'output-area-component';
|
||||
|
||||
@@ -48,6 +49,10 @@ export class OutputAreaComponent extends AngularDisposable implements OnInit {
|
||||
return this.cellModel.trustedMode;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.cellModel.notebookModel.notebookUri;
|
||||
}
|
||||
|
||||
private setFocusAndScroll(node: HTMLElement): void {
|
||||
if (node) {
|
||||
node.focus();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</code-component>
|
||||
</div>
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
|
||||
<div #preview link-handler [isTrusted]="isTrusted" class="notebook-preview" style="flex: 1 1 auto" (dblclick)="toggleEditMode()">
|
||||
<div #preview link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-preview" style="flex: 1 1 auto" (dblclick)="toggleEditMode()">
|
||||
</div>
|
||||
<div #moreactions class="moreActions" style="flex: 0 0 auto; display: flex; flex-flow:column;width: 20px; min-height: 20px; max-height: 20px; padding-top: 0px; orientation: portrait">
|
||||
</div>
|
||||
|
||||
@@ -154,6 +154,10 @@ export class TextCellComponent extends CellView implements OnInit, OnChanges {
|
||||
return this.model.trustedMode;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.model.notebookUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the preview of markdown component with latest changes
|
||||
* If content is empty and in non-edit mode, default it to 'Double-click to edit'
|
||||
|
||||
@@ -24,7 +24,7 @@ import { AngularDisposable } from 'sql/base/node/lifecycle';
|
||||
import { CellTypes, CellType } from 'sql/workbench/parts/notebook/models/contracts';
|
||||
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/workbench/parts/notebook/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, INotebookSection, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { NotebookModel } from 'sql/workbench/parts/notebook/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/workbench/parts/notebook/models/modelFactory';
|
||||
@@ -582,4 +582,48 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
}
|
||||
}
|
||||
|
||||
getSections(): INotebookSection[] {
|
||||
return this.getSectionElements();
|
||||
}
|
||||
|
||||
private getSectionElements(): NotebookSection[] {
|
||||
let headers: NotebookSection[] = [];
|
||||
let el: HTMLElement = this.container.nativeElement;
|
||||
let headerElements = el.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
for (let i = 0; i < headerElements.length; i++) {
|
||||
let headerEl = headerElements[i] as HTMLElement;
|
||||
if (headerEl['id']) {
|
||||
headers.push(new NotebookSection(headerEl));
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
navigateToSection(id: string): void {
|
||||
id = id.toLowerCase();
|
||||
let section = this.getSectionElements().find(s => s.relativeUri && s.relativeUri.toLowerCase() === id);
|
||||
if (section) {
|
||||
// Scroll this section to the top of the header instead of just bringing header into view.
|
||||
let scrollTop = jQuery(section.headerEl).offset().top;
|
||||
(<HTMLElement>this.container.nativeElement).scrollTo({
|
||||
top: scrollTop,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
section.headerEl.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NotebookSection implements INotebookSection {
|
||||
|
||||
constructor(public headerEl: HTMLElement) {
|
||||
}
|
||||
|
||||
get relativeUri(): string {
|
||||
return this.headerEl['id'];
|
||||
}
|
||||
|
||||
get header(): string {
|
||||
return this.headerEl.textContent;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<div style="overflow: hidden; width: 100%; height: 100%; display: flex; flex-flow: row">
|
||||
<div class="icon in-progress" *ngIf="loading === true"></div>
|
||||
<div #output link-handler [isTrusted]="isTrusted" class="notebook-preview" style="flex: 1 1 auto">
|
||||
<div #output link-handler [isTrusted]="isTrusted" [notebookUri]="notebookUri" class="notebook-preview" style="flex: 1 1 auto">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { convertVscodeResourceToFileInSubDirectories, useInProcMarkdown } from '
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/parts/notebook/outputs/notebookMarkdown';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
@Component({
|
||||
selector: MarkdownOutputComponent.SELECTOR,
|
||||
@@ -75,6 +76,10 @@ export class MarkdownOutputComponent extends AngularDisposable implements IMimeC
|
||||
return this._bundleOptions && this._bundleOptions.trusted;
|
||||
}
|
||||
|
||||
public get notebookUri(): URI {
|
||||
return this.cellModel.notebookModel.notebookUri;
|
||||
}
|
||||
|
||||
//Gets sanitizer from ISanitizer interface
|
||||
private get sanitizer(): ISanitizer {
|
||||
if (this._sanitizer) {
|
||||
|
||||
@@ -90,6 +90,12 @@ export interface INotebookService {
|
||||
*/
|
||||
serializeNotebookStateChange(notebookUri: URI, changeType: NotebookChangeType): void;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param notebookUri URI of the notebook to navigate to
|
||||
* @param sectionId ID of the section to navigate to
|
||||
*/
|
||||
navigateTo(notebookUri: URI, sectionId: string): void;
|
||||
}
|
||||
|
||||
export interface INotebookProvider {
|
||||
@@ -117,6 +123,15 @@ export interface INotebookParams extends IBootstrapParams {
|
||||
modelFactory?: ModelFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a section in a notebook as the header text for that section,
|
||||
* the relative URI that can be used to link to it inside Notebook documents
|
||||
*/
|
||||
export interface INotebookSection {
|
||||
header: string;
|
||||
relativeUri: string;
|
||||
}
|
||||
|
||||
export interface INotebookEditor {
|
||||
readonly notebookParams: INotebookParams;
|
||||
readonly id: string;
|
||||
@@ -131,4 +146,6 @@ export interface INotebookEditor {
|
||||
runAllCells(startCell?: ICellModel, endCell?: ICellModel): Promise<boolean>;
|
||||
clearOutput(cell: ICellModel): Promise<boolean>;
|
||||
clearAllOutputs(): Promise<boolean>;
|
||||
getSections(): INotebookSection[];
|
||||
navigateToSection(sectionId: string): void;
|
||||
}
|
||||
|
||||
@@ -619,4 +619,11 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
navigateTo(notebookUri: URI, sectionId: string): void {
|
||||
let editor = this._editors.get(notebookUri.toString());
|
||||
if (editor) {
|
||||
editor.navigateToSection(sectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user