diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 056862a1f8a..8c1029e6b67 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -24,7 +24,7 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory constructor() { } - public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ILineBreaksComputer { const requests: string[] = []; const injectedTexts: (LineInjectedText[] | null)[] = []; return { @@ -33,13 +33,13 @@ export class DOMLineBreaksComputerFactory implements ILineBreaksComputerFactory injectedTexts.push(injectedText); }, finalize: () => { - return createLineBreaks(requests, fontInfo, tabSize, wrappingColumn, wrappingIndent, injectedTexts); + return createLineBreaks(requests, fontInfo, tabSize, wrappingColumn, wrappingIndent, wordBreak, injectedTexts); } }; } } -function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent, injectedTextsPerLine: (LineInjectedText[] | null)[]): (ModelLineProjectionData | null)[] { +function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: number, firstLineBreakColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll', injectedTextsPerLine: (LineInjectedText[] | null)[]): (ModelLineProjectionData | null)[] { function createEmptyLineBreakWithPossiblyInjectedText(requestIdx: number): ModelLineProjectionData | null { const injectedTexts = injectedTextsPerLine[requestIdx]; if (injectedTexts) { @@ -129,7 +129,15 @@ function createLineBreaks(requests: string[], fontInfo: FontInfo, tabSize: numbe containerDomNode.style.position = 'absolute'; containerDomNode.style.top = '10000'; - containerDomNode.style.wordWrap = 'break-word'; + if (wordBreak === 'keepAll') { + // word-break: keep-all; overflow-wrap: anywhere + containerDomNode.style.wordBreak = 'keep-all'; + containerDomNode.style.overflowWrap = 'anywhere'; + } else { + // overflow-wrap: break-word + containerDomNode.style.wordBreak = 'inherit'; + containerDomNode.style.overflowWrap = 'break-word'; + } document.body.appendChild(containerDomNode); const range = document.createRange(); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 4a581f5a5ac..3524a22eafa 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4775,6 +4775,7 @@ export const enum EditorOption { unusualLineTerminators, useShadowDOM, useTabStops, + wordBreak, wordSeparators, wordWrap, wordWrapBreakAfterCharacters, @@ -4782,7 +4783,6 @@ export const enum EditorOption { wordWrapColumn, wordWrapOverride1, wordWrapOverride2, - wordBreak, wrappingIndent, wrappingStrategy, showDeprecated, @@ -5388,6 +5388,18 @@ export const EditorOptions = { EditorOption.useTabStops, 'useTabStops', true, { description: nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.") } )), + wordBreak: register(new EditorStringEnumOption( + EditorOption.wordBreak, 'wordBreak', + 'normal' as 'normal' | 'keepAll', + ['normal', 'keepAll'] as const, + { + markdownEnumDescriptions: [ + nls.localize('wordBreak.normal', "Use the default line break rule."), + nls.localize('wordBreak.keepAll', "Word breaks should not be used for Chinese/Japanese/Korean (CJK) text. Non-CJK text behavior is the same as for normal."), + ], + description: nls.localize('wordBreak', "Controls the word break rules used for Chinese/Japanese/Korean (CJK) text.") + } + )), wordSeparators: register(new EditorStringOption( EditorOption.wordSeparators, 'wordSeparators', USUAL_WORD_SEPARATORS, { description: nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations.") } @@ -5433,23 +5445,6 @@ export const EditorOptions = { // allow-any-unicode-next-line '([{‘“〈《「『【〔([{「£¥$£¥++' )), - wordBreak: register(new EditorStringEnumOption( - EditorOption.wordBreak, 'wordBreak', - 'normal' as 'normal' | 'keepAll', - ['normal', 'keepAll'] as const, - { - markdownEnumDescriptions: [ - nls.localize('wordBreak.normal', "Use the default line break rule."), - nls.localize('wordBreak.keepAll', "Word breaks should not be used for Chinese/Japanese/Korean (CJK) text. Non-CJK text behavior is the same as for normal."), - ], - description: nls.localize({ - key: 'wordBreak', - comment: [ - '' - ] - }, "Sets whether line breaks appear wherever the text would otherwise overflow its content box.") - } - )), wordWrapColumn: register(new EditorIntOption( EditorOption.wordWrapColumn, 'wordWrapColumn', 80, 1, Constants.MAX_SAFE_SMALL_INTEGER, diff --git a/src/vs/editor/common/modelLineProjectionData.ts b/src/vs/editor/common/modelLineProjectionData.ts index e1f87378985..78d14ef17de 100644 --- a/src/vs/editor/common/modelLineProjectionData.ts +++ b/src/vs/editor/common/modelLineProjectionData.ts @@ -329,7 +329,7 @@ export class OutputPosition { } export interface ILineBreaksComputerFactory { - createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer; + createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ILineBreaksComputer; } export interface ILineBreaksComputer { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 9a7fa4e9234..1739085308d 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -291,14 +291,14 @@ export enum EditorOption { unusualLineTerminators = 116, useShadowDOM = 117, useTabStops = 118, - wordSeparators = 119, - wordWrap = 120, - wordWrapBreakAfterCharacters = 121, - wordWrapBreakBeforeCharacters = 122, - wordWrapColumn = 123, - wordWrapOverride1 = 124, - wordWrapOverride2 = 125, - wordBreak = 126, + wordBreak = 119, + wordSeparators = 120, + wordWrap = 121, + wordWrapBreakAfterCharacters = 122, + wordWrapBreakBeforeCharacters = 123, + wordWrapColumn = 124, + wordWrapOverride1 = 125, + wordWrapOverride2 = 126, wrappingIndent = 127, wrappingStrategy = 128, showDeprecated = 129, diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 3cbcafd16f0..05a54a8ab29 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -5,7 +5,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; -import { WrappingIndent, IComputedEditorOptions, EditorOption, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { WrappingIndent, IComputedEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { LineInjectedText } from 'vs/editor/common/textModelEvents'; @@ -16,18 +16,17 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa public static create(options: IComputedEditorOptions): MonospaceLineBreaksComputerFactory { return new MonospaceLineBreaksComputerFactory( options.get(EditorOption.wordWrapBreakBeforeCharacters), - options.get(EditorOption.wordWrapBreakAfterCharacters), - options.get(EditorOption.wordBreak) + options.get(EditorOption.wordWrapBreakAfterCharacters) ); } private readonly classifier: WrappingCharacterClassifier; - constructor(breakBeforeChars: string, breakAfterChars: string, wordBreakMode: IEditorOptions['wordBreak']) { - this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars, wordBreakMode); + constructor(breakBeforeChars: string, breakAfterChars: string) { + this.classifier = new WrappingCharacterClassifier(breakBeforeChars, breakAfterChars); } - public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent): ILineBreaksComputer { + public createLineBreaksComputer(fontInfo: FontInfo, tabSize: number, wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ILineBreaksComputer { const requests: string[] = []; const injectedTexts: (LineInjectedText[] | null)[] = []; const previousBreakingData: (ModelLineProjectionData | null)[] = []; @@ -44,9 +43,9 @@ export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFa const injectedText = injectedTexts[i]; const previousLineBreakData = previousBreakingData[i]; if (previousLineBreakData && !previousLineBreakData.injectionOptions && !injectedText) { - result[i] = createLineBreaksFromPreviousLineBreaks(this.classifier, previousLineBreakData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + result[i] = createLineBreaksFromPreviousLineBreaks(this.classifier, previousLineBreakData, requests[i], tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent, wordBreak); } else { - result[i] = createLineBreaks(this.classifier, requests[i], injectedText, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent); + result[i] = createLineBreaks(this.classifier, requests[i], injectedText, tabSize, wrappingColumn, columnsForFullWidthChar, wrappingIndent, wordBreak); } } arrPool1.length = 0; @@ -66,8 +65,7 @@ const enum CharacterClass { class WrappingCharacterClassifier extends CharacterClassifier { - private readonly isKeepAll: boolean; - constructor(BREAK_BEFORE: string, BREAK_AFTER: string, wordBreakMode: IEditorOptions['wordBreak']) { + constructor(BREAK_BEFORE: string, BREAK_AFTER: string) { super(CharacterClass.NONE); for (let i = 0; i < BREAK_BEFORE.length; i++) { @@ -77,7 +75,6 @@ class WrappingCharacterClassifier extends CharacterClassifier { for (let i = 0; i < BREAK_AFTER.length; i++) { this.set(BREAK_AFTER.charCodeAt(i), CharacterClass.BREAK_AFTER); } - this.isKeepAll = wordBreakMode === 'keepAll'; } public override get(charCode: number): CharacterClass { @@ -88,13 +85,10 @@ class WrappingCharacterClassifier extends CharacterClassifier { // 1. CJK Unified Ideographs (0x4E00 -- 0x9FFF) // 2. CJK Unified Ideographs Extension A (0x3400 -- 0x4DBF) // 3. Hiragana and Katakana (0x3040 -- 0x30FF) - // Except for the case where wordBreak is set to keepAll if ( - !this.isKeepAll && ( - (charCode >= 0x3040 && charCode <= 0x30FF) - || (charCode >= 0x3400 && charCode <= 0x4DBF) - || (charCode >= 0x4E00 && charCode <= 0x9FFF) - ) + (charCode >= 0x3040 && charCode <= 0x30FF) + || (charCode >= 0x3400 && charCode <= 0x4DBF) + || (charCode >= 0x4E00 && charCode <= 0x9FFF) ) { return CharacterClass.BREAK_IDEOGRAPHIC; } @@ -107,7 +101,7 @@ class WrappingCharacterClassifier extends CharacterClassifier { let arrPool1: number[] = []; let arrPool2: number[] = []; -function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: ModelLineProjectionData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ModelLineProjectionData | null { +function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterClassifier, previousBreakingData: ModelLineProjectionData, lineText: string, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ModelLineProjectionData | null { if (firstLineBreakColumn === -1) { return null; } @@ -117,6 +111,8 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla return null; } + const isKeepAll = (wordBreak === 'keepAll'); + const prevBreakingOffsets = previousBreakingData.breakOffsets; const prevBreakingOffsetsVisibleColumn = previousBreakingData.breakOffsetsVisibleColumn; @@ -182,7 +178,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } - if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + if (charStartOffset > lastBreakingOffset && canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } @@ -266,7 +262,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla break; } - if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; break; @@ -359,7 +355,7 @@ function createLineBreaksFromPreviousLineBreaks(classifier: WrappingCharacterCla return previousBreakingData; } -function createLineBreaks(classifier: WrappingCharacterClassifier, _lineText: string, injectedTexts: LineInjectedText[] | null, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent): ModelLineProjectionData | null { +function createLineBreaks(classifier: WrappingCharacterClassifier, _lineText: string, injectedTexts: LineInjectedText[] | null, tabSize: number, firstLineBreakColumn: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): ModelLineProjectionData | null { const lineText = LineInjectedText.applyInjectedText(_lineText, injectedTexts); let injectionOptions: InjectedTextOptions[] | null; @@ -391,6 +387,7 @@ function createLineBreaks(classifier: WrappingCharacterClassifier, _lineText: st return new ModelLineProjectionData(injectionOffsets, injectionOptions, [lineText.length], [], 0); } + const isKeepAll = (wordBreak === 'keepAll'); const wrappedTextIndentLength = computeWrappedTextIndentLength(lineText, tabSize, firstLineBreakColumn, columnsForFullWidthChar, wrappingIndent); const wrappedLineBreakColumn = firstLineBreakColumn - wrappedTextIndentLength; @@ -430,7 +427,7 @@ function createLineBreaks(classifier: WrappingCharacterClassifier, _lineText: st charWidth = computeCharWidth(charCode, visibleColumn, tabSize, columnsForFullWidthChar); } - if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass)) { + if (canBreak(prevCharCode, prevCharCodeClass, charCode, charCodeClass, isKeepAll)) { breakOffset = charStartOffset; breakOffsetVisibleColumn = visibleColumn; } @@ -491,14 +488,14 @@ function tabCharacterWidth(visibleColumn: number, tabSize: number): number { * Kinsoku Shori : Don't break after a leading character, like an open bracket * Kinsoku Shori : Don't break before a trailing character, like a period */ -function canBreak(prevCharCode: number, prevCharCodeClass: CharacterClass, charCode: number, charCodeClass: CharacterClass): boolean { +function canBreak(prevCharCode: number, prevCharCodeClass: CharacterClass, charCode: number, charCodeClass: CharacterClass, isKeepAll: boolean): boolean { return ( charCode !== CharCode.Space && ( (prevCharCodeClass === CharacterClass.BREAK_AFTER && charCodeClass !== CharacterClass.BREAK_AFTER) // break at the end of multiple BREAK_AFTER || (prevCharCodeClass !== CharacterClass.BREAK_BEFORE && charCodeClass === CharacterClass.BREAK_BEFORE) // break at the start of multiple BREAK_BEFORE - || (prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) - || (charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) + || (!isKeepAll && prevCharCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && charCodeClass !== CharacterClass.BREAK_AFTER) + || (!isKeepAll && charCodeClass === CharacterClass.BREAK_IDEOGRAPHIC && prevCharCodeClass !== CharacterClass.BREAK_BEFORE) ) ); } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 98aa89ee45f..9033f5e84db 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -93,6 +93,7 @@ export class ViewModel extends Disposable implements IViewModel { const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); + const wordBreak = options.get(EditorOption.wordBreak); this._lines = new ViewModelLinesFromProjectedModel( this._editorId, @@ -103,7 +104,8 @@ export class ViewModel extends Disposable implements IViewModel { this.model.getOptions().tabSize, wrappingStrategy, wrappingInfo.wrappingColumn, - wrappingIndent + wrappingIndent, + wordBreak ); } @@ -230,8 +232,9 @@ export class ViewModel extends Disposable implements IViewModel { const wrappingStrategy = options.get(EditorOption.wrappingStrategy); const wrappingInfo = options.get(EditorOption.wrappingInfo); const wrappingIndent = options.get(EditorOption.wrappingIndent); + const wordBreak = options.get(EditorOption.wordBreak); - if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) { + if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent, wordBreak)) { eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent()); eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent()); eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null)); diff --git a/src/vs/editor/common/viewModel/viewModelLines.ts b/src/vs/editor/common/viewModel/viewModelLines.ts index f5f7351bccb..96746d3bb9f 100644 --- a/src/vs/editor/common/viewModel/viewModelLines.ts +++ b/src/vs/editor/common/viewModel/viewModelLines.ts @@ -22,7 +22,7 @@ import { ICoordinatesConverter, ViewLineData } from 'vs/editor/common/viewModel' export interface IViewModelLines extends IDisposable { createCoordinatesConverter(): ICoordinatesConverter; - setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean; + setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): boolean; setTabSize(newTabSize: number): boolean; getHiddenAreas(): Range[]; setHiddenAreas(_ranges: readonly Range[]): boolean; @@ -69,6 +69,7 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { private tabSize: number; private wrappingColumn: number; private wrappingIndent: WrappingIndent; + private wordBreak: 'normal' | 'keepAll'; private wrappingStrategy: 'simple' | 'advanced'; private modelLineProjections!: IModelLineProjection[]; @@ -90,6 +91,7 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent, + wordBreak: 'normal' | 'keepAll' ) { this._editorId = editorId; this.model = model; @@ -101,6 +103,7 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; + this.wordBreak = wordBreak; this._constructLines(/*resetHiddenAreas*/true, null); } @@ -269,21 +272,23 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { return true; } - public setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent): boolean { + public setWrappingSettings(fontInfo: FontInfo, wrappingStrategy: 'simple' | 'advanced', wrappingColumn: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll'): boolean { const equalFontInfo = this.fontInfo.equals(fontInfo); const equalWrappingStrategy = (this.wrappingStrategy === wrappingStrategy); const equalWrappingColumn = (this.wrappingColumn === wrappingColumn); const equalWrappingIndent = (this.wrappingIndent === wrappingIndent); - if (equalFontInfo && equalWrappingStrategy && equalWrappingColumn && equalWrappingIndent) { + const equalWordBreak = (this.wordBreak === wordBreak); + if (equalFontInfo && equalWrappingStrategy && equalWrappingColumn && equalWrappingIndent && equalWordBreak) { return false; } - const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingStrategy && !equalWrappingColumn && equalWrappingIndent); + const onlyWrappingColumnChanged = (equalFontInfo && equalWrappingStrategy && !equalWrappingColumn && equalWrappingIndent && equalWordBreak); this.fontInfo = fontInfo; this.wrappingStrategy = wrappingStrategy; this.wrappingColumn = wrappingColumn; this.wrappingIndent = wrappingIndent; + this.wordBreak = wordBreak; let previousLineBreaks: ((ModelLineProjectionData | null)[]) | null = null; if (onlyWrappingColumnChanged) { @@ -304,7 +309,7 @@ export class ViewModelLinesFromProjectedModel implements IViewModelLines { ? this._domLineBreaksComputerFactory : this._monospaceLineBreaksComputerFactory ); - return lineBreaksComputerFactory.createLineBreaksComputer(this.fontInfo, this.tabSize, this.wrappingColumn, this.wrappingIndent); + return lineBreaksComputerFactory.createLineBreaksComputer(this.fontInfo, this.tabSize, this.wrappingColumn, this.wrappingIndent, this.wordBreak); } public onModelFlushed(): void { diff --git a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts index 7a90e7c50f9..06de7665c02 100644 --- a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts +++ b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts @@ -97,7 +97,7 @@ suite('Editor ViewModel - SplitLinesCollection', () => { const wordWrapBreakBeforeCharacters = config.options.get(EditorOption.wordWrapBreakBeforeCharacters); const wrappingIndent = config.options.get(EditorOption.wrappingIndent); const wordBreak = config.options.get(EditorOption.wordBreak); - const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters, wordBreak); + const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters); const model = createTextModel([ 'int main() {', @@ -117,7 +117,8 @@ suite('Editor ViewModel - SplitLinesCollection', () => { model.getOptions().tabSize, 'simple', wrappingInfo.wrappingColumn, - wrappingIndent + wrappingIndent, + wordBreak ); callback(model, linesCollection); @@ -951,7 +952,7 @@ suite('SplitLinesCollection', () => { const wrappingIndent = configuration.options.get(EditorOption.wrappingIndent); const wordBreak = configuration.options.get(EditorOption.wordBreak); - const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters, wordBreak); + const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters); const linesCollection = new ViewModelLinesFromProjectedModel( 1, @@ -962,7 +963,8 @@ suite('SplitLinesCollection', () => { model.getOptions().tabSize, 'simple', wrappingInfo.wrappingColumn, - wrappingIndent + wrappingIndent, + wordBreak ); callback(linesCollection); diff --git a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts index 57db332880e..fead24b2c28 100644 --- a/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/monospaceLineBreaksComputer.test.ts @@ -43,7 +43,7 @@ function toAnnotatedText(text: string, lineBreakData: ModelLineProjectionData | return actualAnnotatedText; } -function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, text: string, previousLineBreakData: ModelLineProjectionData | null): ModelLineProjectionData | null { +function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, columnsForFullWidthChar: number, wrappingIndent: WrappingIndent, wordBreak: 'normal' | 'keepAll', text: string, previousLineBreakData: ModelLineProjectionData | null): ModelLineProjectionData | null { const fontInfo = new FontInfo({ pixelRatio: 1, fontFamily: 'testFontFamily', @@ -61,27 +61,27 @@ function getLineBreakData(factory: ILineBreaksComputerFactory, tabSize: number, wsmiddotWidth: 7, maxDigitWidth: 7 }, false); - const lineBreaksComputer = factory.createLineBreaksComputer(fontInfo, tabSize, breakAfter, wrappingIndent); + const lineBreaksComputer = factory.createLineBreaksComputer(fontInfo, tabSize, breakAfter, wrappingIndent, wordBreak); const previousLineBreakDataClone = previousLineBreakData ? new ModelLineProjectionData(null, null, previousLineBreakData.breakOffsets.slice(0), previousLineBreakData.breakOffsetsVisibleColumn.slice(0), previousLineBreakData.wrappedTextIndentLength) : null; lineBreaksComputer.addRequest(text, null, previousLineBreakDataClone); return lineBreaksComputer.finalize()[0]; } -function assertLineBreaks(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None): ModelLineProjectionData | null { +function assertLineBreaks(factory: ILineBreaksComputerFactory, tabSize: number, breakAfter: number, annotatedText: string, wrappingIndent = WrappingIndent.None, wordBreak: 'normal' | 'keepAll' = 'normal'): ModelLineProjectionData | null { // Create version of `annotatedText` with line break markers removed const text = parseAnnotatedText(annotatedText).text; - const lineBreakData = getLineBreakData(factory, tabSize, breakAfter, 2, wrappingIndent, text, null); + const lineBreakData = getLineBreakData(factory, tabSize, breakAfter, 2, wrappingIndent, wordBreak, text, null); const actualAnnotatedText = toAnnotatedText(text, lineBreakData); assert.strictEqual(actualAnnotatedText, annotatedText); return lineBreakData; } -const wordBreakEditorOptionDefaultValue = EditorOptions.wordBreak.defaultValue; + suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { test('MonospaceLineBreaksComputer', () => { - const factory = new MonospaceLineBreaksComputerFactory('(', '\t).', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('(', '\t).'); // Empty string assertLineBreaks(factory, 4, 5, ''); @@ -140,27 +140,27 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { assert.strictEqual(text, parseAnnotatedText(annotatedText2).text); // check that the direct mapping is ok for 1 - const directLineBreakData1 = getLineBreakData(factory, tabSize, breakAfter1, columnsForFullWidthChar, wrappingIndent, text, null); + const directLineBreakData1 = getLineBreakData(factory, tabSize, breakAfter1, columnsForFullWidthChar, wrappingIndent, 'normal', text, null); assert.strictEqual(toAnnotatedText(text, directLineBreakData1), annotatedText1); // check that the direct mapping is ok for 2 - const directLineBreakData2 = getLineBreakData(factory, tabSize, breakAfter2, columnsForFullWidthChar, wrappingIndent, text, null); + const directLineBreakData2 = getLineBreakData(factory, tabSize, breakAfter2, columnsForFullWidthChar, wrappingIndent, 'normal', text, null); assert.strictEqual(toAnnotatedText(text, directLineBreakData2), annotatedText2); // check that going from 1 to 2 is ok - const lineBreakData2from1 = getLineBreakData(factory, tabSize, breakAfter2, columnsForFullWidthChar, wrappingIndent, text, directLineBreakData1); + const lineBreakData2from1 = getLineBreakData(factory, tabSize, breakAfter2, columnsForFullWidthChar, wrappingIndent, 'normal', text, directLineBreakData1); assert.strictEqual(toAnnotatedText(text, lineBreakData2from1), annotatedText2); assertLineBreakDataEqual(lineBreakData2from1, directLineBreakData2); // check that going from 2 to 1 is ok - const lineBreakData1from2 = getLineBreakData(factory, tabSize, breakAfter1, columnsForFullWidthChar, wrappingIndent, text, directLineBreakData2); + const lineBreakData1from2 = getLineBreakData(factory, tabSize, breakAfter1, columnsForFullWidthChar, wrappingIndent, 'normal', text, directLineBreakData2); assert.strictEqual(toAnnotatedText(text, lineBreakData1from2), annotatedText1); assertLineBreakDataEqual(lineBreakData1from2, directLineBreakData1); } test('MonospaceLineBreaksComputer incremental 1', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertIncrementalLineBreaks( factory, 'just some text and more', 4, @@ -217,7 +217,7 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { }); test('issue #95686: CRITICAL: loop forever on the monospaceLineBreaksComputer', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertIncrementalLineBreaks( factory, ' ', @@ -229,7 +229,7 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { }); test('issue #110392: Occasional crash when resize with panel on the right', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertIncrementalLineBreaks( factory, '你好 **hello** **hello** **hello-world** hey there!', @@ -242,7 +242,7 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { }); test('MonospaceLineBreaksComputer - CJK and Kinsoku Shori', () => { - const factory = new MonospaceLineBreaksComputerFactory('(', '\t)', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('(', '\t)'); assertLineBreaks(factory, 4, 5, 'aa \u5b89|\u5b89'); assertLineBreaks(factory, 4, 5, '\u3042 \u5b89|\u5b89'); assertLineBreaks(factory, 4, 5, '\u3042\u3042|\u5b89\u5b89'); @@ -252,64 +252,64 @@ suite('Editor ViewModel - MonospaceLineBreaksComputer', () => { }); test('MonospaceLineBreaksComputer - WrappingIndent.Same', () => { - const factory = new MonospaceLineBreaksComputerFactory('', '\t ', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('', '\t '); assertLineBreaks(factory, 4, 38, ' *123456789012345678901234567890123456|7890', WrappingIndent.Same); }); test('issue #16332: Scroll bar overlaying on top of text', () => { - const factory = new MonospaceLineBreaksComputerFactory('', '\t ', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('', '\t '); assertLineBreaks(factory, 4, 24, 'a/ very/long/line/of/tex|t/that/expands/beyon|d/your/typical/line/|of/code/', WrappingIndent.Indent); }); test('issue #35162: wrappingIndent not consistently working', () => { - const factory = new MonospaceLineBreaksComputerFactory('', '\t ', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('', '\t '); const mapper = assertLineBreaks(factory, 4, 24, ' t h i s |i s |a l |o n |g l |i n |e', WrappingIndent.Indent); assert.strictEqual(mapper!.wrappedTextIndentLength, ' '.length); }); test('issue #75494: surrogate pairs', () => { - const factory = new MonospaceLineBreaksComputerFactory('\t', ' ', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('\t', ' '); assertLineBreaks(factory, 4, 49, '🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼🐇👬🌖🌞🏇🍼|🐇👬', WrappingIndent.Same); }); test('issue #75494: surrogate pairs overrun 1', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertLineBreaks(factory, 4, 4, '🐇👬|&|🌞🌖', WrappingIndent.Same); }); test('issue #75494: surrogate pairs overrun 2', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertLineBreaks(factory, 4, 17, 'factory, |"xtxtfunc|(x"🌞🏇🍼🌞🏇🍼🐇|&👬🌖🌞👬🌖🌞🏇🍼|🐇👬x"', WrappingIndent.Same); }); test('MonospaceLineBreaksComputer - WrappingIndent.DeepIndent', () => { - const factory = new MonospaceLineBreaksComputerFactory('', '\t ', wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory('', '\t '); const mapper = assertLineBreaks(factory, 4, 26, ' W e A r e T e s t |i n g D e |e p I n d |e n t a t |i o n', WrappingIndent.DeepIndent); assert.strictEqual(mapper!.wrappedTextIndentLength, ' '.length); }); test('issue #33366: Word wrap algorithm behaves differently around punctuation', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertLineBreaks(factory, 4, 23, 'this is a line of |text, text that sits |on a line', WrappingIndent.Same); }); test('issue #152773: Word wrap algorithm behaves differently with bracket followed by comma', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertLineBreaks(factory, 4, 24, 'this is a line of |(text), text that sits |on a line', WrappingIndent.Same); }); test('issue #112382: Word wrap doesn\'t work well with control characters', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); assertLineBreaks(factory, 4, 6, '\x06\x06\x06|\x06\x06\x06', WrappingIndent.Same); }); test('Word break work well with Chinese/Japanese/Korean (CJK) text when setting normal', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, wordBreakEditorOptionDefaultValue); - assertLineBreaks(factory, 4, 5, '你好|1111', WrappingIndent.Same); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineBreaks(factory, 4, 5, '你好|1111', WrappingIndent.Same, 'normal'); }); test('Word break work well with Chinese/Japanese/Korean (CJK) text when setting keepAll', () => { - const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue, 'keepAll'); - assertLineBreaks(factory, 4, 8, '你好1111', WrappingIndent.Same); + const factory = new MonospaceLineBreaksComputerFactory(EditorOptions.wordWrapBreakBeforeCharacters.defaultValue, EditorOptions.wordWrapBreakAfterCharacters.defaultValue); + assertLineBreaks(factory, 4, 8, '你好1111', WrappingIndent.Same, 'keepAll'); }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ac4330bbfaa..2341673133d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4668,14 +4668,14 @@ declare namespace monaco.editor { unusualLineTerminators = 116, useShadowDOM = 117, useTabStops = 118, - wordSeparators = 119, - wordWrap = 120, - wordWrapBreakAfterCharacters = 121, - wordWrapBreakBeforeCharacters = 122, - wordWrapColumn = 123, - wordWrapOverride1 = 124, - wordWrapOverride2 = 125, - wordBreak = 126, + wordBreak = 119, + wordSeparators = 120, + wordWrap = 121, + wordWrapBreakAfterCharacters = 122, + wordWrapBreakBeforeCharacters = 123, + wordWrapColumn = 124, + wordWrapOverride1 = 125, + wordWrapOverride2 = 126, wrappingIndent = 127, wrappingStrategy = 128, showDeprecated = 129, @@ -4809,11 +4809,11 @@ declare namespace monaco.editor { unusualLineTerminators: IEditorOption; useShadowDOM: IEditorOption; useTabStops: IEditorOption; + wordBreak: IEditorOption; wordSeparators: IEditorOption; wordWrap: IEditorOption; wordWrapBreakAfterCharacters: IEditorOption; wordWrapBreakBeforeCharacters: IEditorOption; - wordBreak: IEditorOption; wordWrapColumn: IEditorOption; wordWrapOverride1: IEditorOption; wordWrapOverride2: IEditorOption;