|
| 1 | +/*--------------------------------------------------------------------------------------------- |
| 2 | + * Copyright (c) Microsoft Corporation. All rights reserved. |
| 3 | + * Licensed under the MIT License. See License.txt in the project root for license information. |
| 4 | + *--------------------------------------------------------------------------------------------*/ |
| 5 | + |
| 6 | +import * as DOM from 'vs/base/browser/dom'; |
| 7 | +import { Color } from 'vs/base/common/color'; |
| 8 | +import * as platform from 'vs/base/common/platform'; |
| 9 | +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; |
| 10 | +import { EditorOption } from 'vs/editor/common/config/editorOptions'; |
| 11 | +import { Range } from 'vs/editor/common/core/range'; |
| 12 | +import * as languages from 'vs/editor/common/languages'; |
| 13 | +import { tokenizeLineToHTML } from 'vs/editor/common/languages/textToHtmlTokenizer'; |
| 14 | +import { ITextModel } from 'vs/editor/common/model'; |
| 15 | +import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; |
| 16 | + |
| 17 | +class EditorTextRenderer { |
| 18 | + |
| 19 | + private static _ttPolicy = window.trustedTypes?.createPolicy('cellRendererEditorText', { |
| 20 | + createHTML(input) { return input; } |
| 21 | + }); |
| 22 | + |
| 23 | + getRichText(editor: ICodeEditor, modelRange: Range): HTMLElement | null { |
| 24 | + const model = editor.getModel(); |
| 25 | + if (!model) { |
| 26 | + return null; |
| 27 | + } |
| 28 | + |
| 29 | + const colorMap = this.getDefaultColorMap(); |
| 30 | + const fontInfo = editor.getOptions().get(EditorOption.fontInfo); |
| 31 | + const fontFamilyVar = '--notebook-editor-font-family'; |
| 32 | + const fontSizeVar = '--notebook-editor-font-size'; |
| 33 | + const fontWeightVar = '--notebook-editor-font-weight'; |
| 34 | + |
| 35 | + const style = `` |
| 36 | + + `color: ${colorMap[languages.ColorId.DefaultForeground]};` |
| 37 | + + `background-color: ${colorMap[languages.ColorId.DefaultBackground]};` |
| 38 | + + `font-family: var(${fontFamilyVar});` |
| 39 | + + `font-weight: var(${fontWeightVar});` |
| 40 | + + `font-size: var(${fontSizeVar});` |
| 41 | + + `line-height: ${fontInfo.lineHeight}px;` |
| 42 | + + `white-space: pre;`; |
| 43 | + |
| 44 | + const element = DOM.$('div', { style }); |
| 45 | + |
| 46 | + const fontSize = fontInfo.fontSize; |
| 47 | + const fontWeight = fontInfo.fontWeight; |
| 48 | + element.style.setProperty(fontFamilyVar, fontInfo.fontFamily); |
| 49 | + element.style.setProperty(fontSizeVar, `${fontSize}px`); |
| 50 | + element.style.setProperty(fontWeightVar, fontWeight); |
| 51 | + |
| 52 | + const linesHtml = this.getRichTextLinesAsHtml(model, modelRange, colorMap); |
| 53 | + element.innerHTML = linesHtml as string; |
| 54 | + return element; |
| 55 | + } |
| 56 | + |
| 57 | + private getRichTextLinesAsHtml(model: ITextModel, modelRange: Range, colorMap: string[]): string | TrustedHTML { |
| 58 | + const startLineNumber = modelRange.startLineNumber; |
| 59 | + const startColumn = modelRange.startColumn; |
| 60 | + const endLineNumber = modelRange.endLineNumber; |
| 61 | + const endColumn = modelRange.endColumn; |
| 62 | + |
| 63 | + const tabSize = model.getOptions().tabSize; |
| 64 | + |
| 65 | + let result = ''; |
| 66 | + |
| 67 | + for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) { |
| 68 | + const lineTokens = model.getLineTokens(lineNumber); |
| 69 | + const lineContent = lineTokens.getLineContent(); |
| 70 | + const startOffset = (lineNumber === startLineNumber ? startColumn - 1 : 0); |
| 71 | + const endOffset = (lineNumber === endLineNumber ? endColumn - 1 : lineContent.length); |
| 72 | + |
| 73 | + if (lineContent === '') { |
| 74 | + result += '<br>'; |
| 75 | + } else { |
| 76 | + result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows); |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + return EditorTextRenderer._ttPolicy?.createHTML(result) ?? result; |
| 81 | + } |
| 82 | + |
| 83 | + private getDefaultColorMap(): string[] { |
| 84 | + const colorMap = languages.TokenizationRegistry.getColorMap(); |
| 85 | + const result: string[] = ['#000000']; |
| 86 | + if (colorMap) { |
| 87 | + for (let i = 1, len = colorMap.length; i < len; i++) { |
| 88 | + result[i] = Color.Format.CSS.formatHex(colorMap[i]); |
| 89 | + } |
| 90 | + } |
| 91 | + return result; |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +export class CodeCellDragImageRenderer { |
| 96 | + getDragImage(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement { |
| 97 | + let dragImage = this.getDragImageImpl(templateData, editor, type); |
| 98 | + if (!dragImage) { |
| 99 | + // TODO@roblourens I don't think this can happen |
| 100 | + dragImage = document.createElement('div'); |
| 101 | + dragImage.textContent = '1 cell'; |
| 102 | + } |
| 103 | + |
| 104 | + return dragImage; |
| 105 | + } |
| 106 | + |
| 107 | + private getDragImageImpl(templateData: BaseCellRenderTemplate, editor: ICodeEditor, type: 'code' | 'markdown'): HTMLElement | null { |
| 108 | + const dragImageContainer = templateData.container.cloneNode(true) as HTMLElement; |
| 109 | + dragImageContainer.classList.forEach(c => dragImageContainer.classList.remove(c)); |
| 110 | + dragImageContainer.classList.add('cell-drag-image', 'monaco-list-row', 'focused', `${type}-cell-row`); |
| 111 | + |
| 112 | + const editorContainer: HTMLElement | null = dragImageContainer.querySelector('.cell-editor-container'); |
| 113 | + if (!editorContainer) { |
| 114 | + return null; |
| 115 | + } |
| 116 | + |
| 117 | + const richEditorText = new EditorTextRenderer().getRichText(editor, new Range(1, 1, 1, 1000)); |
| 118 | + if (!richEditorText) { |
| 119 | + return null; |
| 120 | + } |
| 121 | + DOM.reset(editorContainer, richEditorText); |
| 122 | + |
| 123 | + return dragImageContainer; |
| 124 | + } |
| 125 | +} |
0 commit comments