From 3c33a7ba9f4e17d09240974907cf50d0d8899aa6 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 15:22:11 +0200 Subject: [PATCH 01/22] Soft wrap based on default character width --- spec/display-buffer-spec.coffee | 6 ++++-- src/display-buffer.coffee | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 7ed97f0d0..a6b6242d4 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -52,10 +52,11 @@ describe "DisplayBuffer", -> displayBuffer2 = new DisplayBuffer({buffer, tabLength}) buffer.setText("testing") - describe "soft wrapping", -> + fdescribe "soft wrapping", -> beforeEach -> - displayBuffer.setSoftWrapped(true) + displayBuffer.setDefaultCharWidth(1) displayBuffer.setEditorWidthInChars(50) + displayBuffer.setSoftWrapped(true) changeHandler.reset() describe "rendering of soft-wrapped lines", -> @@ -233,6 +234,7 @@ describe "DisplayBuffer", -> it "correctly renders the original wrapped line", -> buffer = atom.project.buildBufferSync(null, '') displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30}) + displayBuffer.setDefaultCharWidth(1) displayBuffer.setSoftWrapped(true) buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.") diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 339fa11c1..a545235c2 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -262,6 +262,33 @@ class DisplayBuffer extends Model else @getEditorWidthInChars() + getMaxLengthPerLine: -> + if @configSettings.softWrapAtPreferredLineLength + Math.min(@getEditorWidthInChars(), @configSettings.preferredLineLength) + else + @getEditorWidthInChars() + + getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> + lineMaxWidth = @getMaxLengthPerLine() * @getDefaultCharWidth() + iterator = tokenizedLine.getTokenIterator() + column = 0 + currentWidth = 0 + while iterator.next() + textIndex = 0 + text = iterator.getText() + while textIndex < text.length + if iterator.isPairedCharacter() + charLength = 2 + else + charLength = 1 + + charWidth = @getDefaultCharWidth() + return column if currentWidth + charWidth > lineMaxWidth + currentWidth += charWidth + column += charLength + textIndex += charLength + column + # Gets the screen line for the given screen row. # # * `screenRow` - A {Number} indicating the screen row. @@ -958,7 +985,7 @@ class DisplayBuffer extends Model else softWraps = 0 if @isSoftWrapped() - while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumn()) + while wrapScreenColumn = tokenizedLine.findWrapColumn(@getSoftWrapColumnForTokenizedLine(tokenizedLine)) [wrappedLine, tokenizedLine] = tokenizedLine.softWrapAt( wrapScreenColumn, @configSettings.softWrapHangingIndent From b2a7f4a28e092b98234befbd5979c81a15756c8c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 15:52:15 +0200 Subject: [PATCH 02/22] Create utility function to recognize double width characters --- spec/text-utils-spec.coffee | 13 +++++++++++++ src/text-utils.coffee | 26 +++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index 6a03bb02f..a8bdbc2f7 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -44,3 +44,16 @@ describe 'text utilities', -> expect(textUtils.isPairedCharacter('ae\u0301c', 2)).toBe false expect(textUtils.isPairedCharacter('ae\u0301c', 3)).toBe false expect(textUtils.isPairedCharacter('ae\u0301c', 4)).toBe false + + describe ".isDoubleWidthCharacter(character)", -> + it "returns true when the character is either japanese, korean, chinese or a full width form", -> + expect(textUtils.isDoubleWidthCharacter("我")).toBe(true) + + expect(textUtils.isDoubleWidthCharacter("私")).toBe(true) + + expect(textUtils.isDoubleWidthCharacter("우")).toBe(true) + + expect(textUtils.isDoubleWidthCharacter("B")).toBe(true) + expect(textUtils.isDoubleWidthCharacter(",")).toBe(true) + + expect(textUtils.isDoubleWidthCharacter("a")).toBe(false) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index 37955dcd6..1995094fe 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -57,6 +57,30 @@ isPairedCharacter = (string, index=0) -> isVariationSequence(charCodeA, charCodeB) or isCombinedCharacter(charCodeA, charCodeB) +isKoreanCharacter = (charCode) -> + 0xAC00 <= charCode <= 0xD7A3 or + 0x1100 <= charCode <= 0x11FF or + 0x3130 <= charCode <= 0x318F or + 0xA960 <= charCode <= 0xA97F or + 0xD7B0 <= charCode <= 0xD7FF + +isJapaneseCharacter = (charCode) -> + 0x3000 <= charCode <= 0x30ff + +isCjkUnifiedIdeograph = (charCode) -> + 0x4e00 <= charCode <= 0x9faf + +isFullWidthForm = (charCode) -> + 0xFF01 <= charCode <= 0xFF5E + +isDoubleWidthCharacter = (character) -> + charCode = character.charCodeAt(0) + + isKoreanCharacter(charCode) or + isJapaneseCharacter(charCode) or + isCjkUnifiedIdeograph(charCode) or + isFullWidthForm(charCode) + # Does the given string contain at least surrogate pair, variation sequence, # or combined character? # @@ -70,4 +94,4 @@ hasPairedCharacter = (string) -> index++ false -module.exports = {isPairedCharacter, hasPairedCharacter} +module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter} From c2ee942df14b4863cb49c6f28f34bef49d39d127 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 16:24:08 +0200 Subject: [PATCH 03/22] Take double width chars into account when soft wrapping --- spec/display-buffer-spec.coffee | 10 ++++++++++ src/display-buffer.coffee | 13 ++++++++++++- src/token-iterator.coffee | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index a6b6242d4..69d78bf70 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -60,6 +60,16 @@ describe "DisplayBuffer", -> changeHandler.reset() describe "rendering of soft-wrapped lines", -> + describe "when there are double width characters", -> + it "takes them into account when finding the soft wrap column", -> + displayBuffer.setDoubleWidthCharWidth(5) + buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为우리포럼hello world this is a pretty long latin line") + + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方") + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe(",数千名学生12345业余爱") + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为우리포럼hello ") + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("world this is a pretty long latin line") + describe "when editor.softWrapAtPreferredLineLength is set", -> it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", -> atom.config.set('editor.preferredLineLength', 100) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index a545235c2..948101385 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -195,6 +195,12 @@ class DisplayBuffer extends Model @defaultCharWidth = defaultCharWidth defaultCharWidth + getDoubleWidthCharWidth: -> @doubleWidthCharWidth + setDoubleWidthCharWidth: (doubleWidthCharWidth) -> + if doubleWidthCharWidth isnt @doubleWidthCharWidth + @doubleWidthCharWidth = doubleWidthCharWidth + doubleWidthCharWidth + getCursorWidth: -> 1 scrollToScreenRange: (screenRange, options = {}) -> @@ -282,8 +288,13 @@ class DisplayBuffer extends Model else charLength = 1 - charWidth = @getDefaultCharWidth() + if iterator.hasDoubleWidthCharacterAt(textIndex) + charWidth = @getDoubleWidthCharWidth() + else + charWidth = @getDefaultCharWidth() + return column if currentWidth + charWidth > lineMaxWidth + currentWidth += charWidth column += charLength textIndex += charLength diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index de874d499..4c4686635 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -1,4 +1,5 @@ {SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' +{isDoubleWidthCharacter} = require './text-utils' module.exports = class TokenIterator @@ -82,5 +83,8 @@ class TokenIterator isPairedCharacter: -> @line.specialTokens[@index] is PairedCharacter + hasDoubleWidthCharacterAt: (charIndex) -> + isDoubleWidthCharacter(@getText()[charIndex]) + isAtomic: -> @isSoftTab() or @isHardTab() or @isSoftWrapIndentation() or @isPairedCharacter() From 20b0523833e45060ce1add1cad1b28540f0ddbb7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 16:41:27 +0200 Subject: [PATCH 04/22] :green_heart: --- spec/display-buffer-spec.coffee | 4 +++- spec/text-editor-component-spec.coffee | 2 ++ spec/text-editor-presenter-spec.coffee | 5 +++++ spec/text-editor-spec.coffee | 12 ++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 69d78bf70..bfe2e12ab 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -52,7 +52,7 @@ describe "DisplayBuffer", -> displayBuffer2 = new DisplayBuffer({buffer, tabLength}) buffer.setText("testing") - fdescribe "soft wrapping", -> + describe "soft wrapping", -> beforeEach -> displayBuffer.setDefaultCharWidth(1) displayBuffer.setEditorWidthInChars(50) @@ -648,6 +648,7 @@ describe "DisplayBuffer", -> beforeEach -> tabLength = 4 + displayBuffer.setDefaultCharWidth(1) displayBuffer.setTabLength(tabLength) displayBuffer.setSoftWrapped(true) displayBuffer.setEditorWidthInChars(50) @@ -765,6 +766,7 @@ describe "DisplayBuffer", -> it "correctly translates positions on soft wrapped lines containing tabs", -> buffer.setText('\t\taa bb cc dd ee ff gg') displayBuffer.setSoftWrapped(true) + displayBuffer.setDefaultCharWidth(1) displayBuffer.setEditorWidthInChars(10) expect(displayBuffer.screenPositionForBufferPosition([0, 10], wrapAtSoftNewlines: true)).toEqual [1, 4] expect(displayBuffer.bufferPositionForScreenPosition([1, 0])).toEqual [0, 9] diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index f6936e4c4..11454aaef 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -3208,7 +3208,9 @@ describe "TextEditorComponent", -> atom.config.set 'editor.preferredLineLength', 17, scopeSelector: '.source.coffee' atom.config.set 'editor.softWrapAtPreferredLineLength', true, scopeSelector: '.source.coffee' + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(20) + coffeeEditor.setDefaultCharWidth(1) coffeeEditor.setEditorWidthInChars(20) it "wraps lines when editor.softWrap is true for a matching scope", -> diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 541599ea8..acb9c29ae 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -1258,6 +1258,7 @@ describe "TextEditorPresenter", -> it "only applies decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> editor.setText("a line that wraps, ok") editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(16) marker = editor.markBufferRange([[0, 0], [0, 2]]) editor.decorateMarker(marker, type: 'line', class: 'a') @@ -2243,6 +2244,7 @@ describe "TextEditorPresenter", -> it "contains states for line numbers that are visible on screen", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2) @@ -2258,6 +2260,7 @@ describe "TextEditorPresenter", -> it "updates when the editor's content changes", -> editor.foldBufferRow(4) editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) presenter = buildPresenter(explicitHeight: 35, scrollTop: 30, tileSize: 2) @@ -2288,6 +2291,7 @@ describe "TextEditorPresenter", -> it "correctly handles the first screen line being soft-wrapped", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(30) presenter = buildPresenter(explicitHeight: 25, scrollTop: 50, tileSize: 2) @@ -2416,6 +2420,7 @@ describe "TextEditorPresenter", -> it "only applies line-number decorations to screen rows that are spanned by their marker when lines are soft-wrapped", -> editor.setText("a line that wraps, ok") editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(16) marker = editor.markBufferRange([[0, 0], [0, 2]]) editor.decorateMarker(marker, type: 'line-number', class: 'a') diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index e95818900..daba04714 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -272,6 +272,7 @@ describe "TextEditor", -> describe "when soft-wrap is enabled and code is folded", -> beforeEach -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) editor.createFold(2, 3) @@ -327,6 +328,7 @@ describe "TextEditor", -> describe "when the cursor was moved down from the beginning of an indented soft-wrapped line", -> it "moves to the beginning of the previous line", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) editor.setCursorScreenPosition([3, 0]) @@ -379,6 +381,7 @@ describe "TextEditor", -> describe "when the cursor is at the beginning of an indented soft-wrapped line", -> it "moves to the beginning of the line's continuation on the next screen row", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) editor.setCursorScreenPosition([3, 0]) @@ -446,6 +449,7 @@ describe "TextEditor", -> describe "when line is wrapped and follow previous line indentation", -> beforeEach -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(50) it "wraps to the end of the previous line", -> @@ -604,6 +608,7 @@ describe "TextEditor", -> describe "when soft wrap is on", -> it "moves cursor to the beginning of the screen line", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([1, 2]) editor.moveToEndOfScreenLine() @@ -623,6 +628,7 @@ describe "TextEditor", -> describe ".moveToBeginningOfLine()", -> it "moves cursor to the beginning of the buffer line", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([1, 2]) editor.moveToBeginningOfLine() @@ -632,6 +638,7 @@ describe "TextEditor", -> describe ".moveToEndOfLine()", -> it "moves cursor to the end of the buffer line", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([0, 2]) editor.moveToEndOfLine() @@ -642,6 +649,7 @@ describe "TextEditor", -> describe "when soft wrap is on", -> it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition [2, 5] editor.addCursorAtScreenPosition [8, 7] @@ -1548,6 +1556,7 @@ describe "TextEditor", -> describe "when lines are soft-wrapped", -> beforeEach -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(40) it "skips soft-wrap indentation tokens", -> @@ -1633,6 +1642,7 @@ describe "TextEditor", -> it "can add selections to soft-wrapped line segments", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(40) editor.setSelectedScreenRange([[4, 10], [4, 15]]) @@ -1659,6 +1669,7 @@ describe "TextEditor", -> describe "when lines are soft-wrapped", -> beforeEach -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(40) it "skips soft-wrap indentation tokens", -> @@ -2703,6 +2714,7 @@ describe "TextEditor", -> describe "when soft wrap is on", -> it "cuts up to the end of the line", -> editor.setSoftWrapped(true) + editor.setDefaultCharWidth(1) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([2, 2]) editor.cutToEndOfLine() From 27dc3da9e5ac871b4519555abaf8152b46e2311a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 17:53:36 +0200 Subject: [PATCH 05/22] :art: Conflate setting default and double-width chars width Because they will always change together, and having two different setters seems highly error-prone. --- spec/display-buffer-spec.coffee | 2 +- src/display-buffer.coffee | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index bfe2e12ab..ee0197903 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -62,7 +62,7 @@ describe "DisplayBuffer", -> describe "rendering of soft-wrapped lines", -> describe "when there are double width characters", -> it "takes them into account when finding the soft wrap column", -> - displayBuffer.setDoubleWidthCharWidth(5) + displayBuffer.setDefaultCharWidth(1, 5) buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为우리포럼hello world this is a pretty long latin line") expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方") diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 948101385..aa70ea554 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -189,17 +189,13 @@ class DisplayBuffer extends Model getLineHeightInPixels: -> @lineHeightInPixels setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels - getDefaultCharWidth: -> @defaultCharWidth - setDefaultCharWidth: (defaultCharWidth) -> - if defaultCharWidth isnt @defaultCharWidth - @defaultCharWidth = defaultCharWidth - defaultCharWidth - getDoubleWidthCharWidth: -> @doubleWidthCharWidth - setDoubleWidthCharWidth: (doubleWidthCharWidth) -> - if doubleWidthCharWidth isnt @doubleWidthCharWidth + getDefaultCharWidth: -> @defaultCharWidth + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth=defaultCharWidth) -> + if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth + @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth - doubleWidthCharWidth + defaultCharWidth getCursorWidth: -> 1 From 4c66341624d4447a3fd9519bb2d30abde57cabb9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:08:35 +0200 Subject: [PATCH 06/22] Measure double width char widths --- spec/text-editor-component-spec.coffee | 10 ++++++++++ src/lines-component.coffee | 18 +++++++++++++++--- src/text-editor-presenter.coffee | 7 ++++--- src/text-editor.coffee | 5 ++++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 11454aaef..9032598e8 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2891,6 +2891,16 @@ describe "TextEditorComponent", -> expect(editor.consolidateSelections).toHaveBeenCalled() expect(event.abortKeyBinding).toHaveBeenCalled() + describe "when changing the font", -> + it "measures the default char width and the double width char width", -> + expect(editor.getDefaultCharWidth()).toBe(12) + + component.setFontSize(10) + nextAnimationFrame() + + expect(editor.getDefaultCharWidth()).toBe(6) + expect(editor.getDoubleWidthCharWidth()).toBe(10) + describe "hiding and showing the editor", -> describe "when the editor is hidden when it is mounted", -> it "defers measurement and rendering until the editor becomes visible", -> diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 2a721a0aa..8a97ccbfd 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -7,7 +7,9 @@ DummyLineNode.className = 'line' DummyLineNode.style.position = 'absolute' DummyLineNode.style.visibility = 'hidden' DummyLineNode.appendChild(document.createElement('span')) -DummyLineNode.firstChild.textContent = 'x' +DummyLineNode.firstChild.textContent = 'xフ' + +RangeForMeasurement = document.createRange() module.exports = class LinesComponent extends TiledComponent @@ -76,12 +78,22 @@ class LinesComponent extends TiledComponent measureLineHeightAndDefaultCharWidth: -> @domNode.appendChild(DummyLineNode) + textNode = DummyLineNode.firstChild.childNodes[0] + lineHeightInPixels = DummyLineNode.getBoundingClientRect().height - charWidth = DummyLineNode.firstChild.getBoundingClientRect().width + + RangeForMeasurement.setStart(textNode, 0) + RangeForMeasurement.setEnd(textNode, 1) + defaultCharWidth = RangeForMeasurement.getBoundingClientRect().width + + RangeForMeasurement.setStart(textNode, 1) + RangeForMeasurement.setEnd(textNode, 2) + doubleWidthCharWidth = RangeForMeasurement.getBoundingClientRect().width + @domNode.removeChild(DummyLineNode) @presenter.setLineHeight(lineHeightInPixels) - @presenter.setBaseCharacterWidth(charWidth) + @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth) lineNodeForLineIdAndScreenRow: (lineId, screenRow) -> tile = @presenter.tileForRow(screenRow) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index efa7845fe..92ccb2d56 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1122,10 +1122,11 @@ class TextEditorPresenter @mouseWheelScreenRow = screenRow @didStartScrolling() - setBaseCharacterWidth: (baseCharacterWidth) -> - unless @baseCharacterWidth is baseCharacterWidth + setBaseCharacterWidth: (baseCharacterWidth, doubleWidthCharWidth) -> + unless @baseCharacterWidth is baseCharacterWidth and @doubleWidthCharWidth is doubleWidthCharWidth @baseCharacterWidth = baseCharacterWidth - @model.setDefaultCharWidth(baseCharacterWidth) + @doubleWidthCharWidth = doubleWidthCharWidth + @model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth) @characterWidthsChanged() characterWidthsChanged: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 4d517ac99..e4b0c8a37 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2976,8 +2976,11 @@ class TextEditor extends Model getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) + getDoubleWidthCharWidth: -> @displayBuffer.getDoubleWidthCharWidth() + getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth() - setDefaultCharWidth: (defaultCharWidth) -> @displayBuffer.setDefaultCharWidth(defaultCharWidth) + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth=defaultCharWidth) -> + @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth) setHeight: (height, reentrant=false) -> if reentrant From 4818d47980b96b7bd98ce99da992f58b7286513e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:10:26 +0200 Subject: [PATCH 07/22] Update wrapped screen lines when default width changes --- spec/display-buffer-spec.coffee | 4 ++-- src/display-buffer.coffee | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index ee0197903..aafca68fe 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -54,16 +54,16 @@ describe "DisplayBuffer", -> describe "soft wrapping", -> beforeEach -> - displayBuffer.setDefaultCharWidth(1) displayBuffer.setEditorWidthInChars(50) displayBuffer.setSoftWrapped(true) + displayBuffer.setDefaultCharWidth(1) changeHandler.reset() describe "rendering of soft-wrapped lines", -> describe "when there are double width characters", -> it "takes them into account when finding the soft wrap column", -> - displayBuffer.setDefaultCharWidth(1, 5) buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为우리포럼hello world this is a pretty long latin line") + displayBuffer.setDefaultCharWidth(1, 5) expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方") expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe(",数千名学生12345业余爱") diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index aa70ea554..60dedb9f8 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -195,6 +195,7 @@ class DisplayBuffer extends Model if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth + @updateWrappedScreenLines() if @isSoftWrapped() defaultCharWidth getCursorWidth: -> 1 From 43229a1b9eed0671b2e46652ea0a53f4c770339f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:35:49 +0200 Subject: [PATCH 08/22] :art: --- src/display-buffer.coffee | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 60dedb9f8..3d960e2d3 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -195,7 +195,7 @@ class DisplayBuffer extends Model if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth - @updateWrappedScreenLines() if @isSoftWrapped() + @updateWrappedScreenLines() if @isSoftWrapped() and @getWidth()? defaultCharWidth getCursorWidth: -> 1 @@ -265,14 +265,8 @@ class DisplayBuffer extends Model else @getEditorWidthInChars() - getMaxLengthPerLine: -> - if @configSettings.softWrapAtPreferredLineLength - Math.min(@getEditorWidthInChars(), @configSettings.preferredLineLength) - else - @getEditorWidthInChars() - getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> - lineMaxWidth = @getMaxLengthPerLine() * @getDefaultCharWidth() + lineMaxWidth = Math.round(@getSoftWrapColumn() * @getDefaultCharWidth()) iterator = tokenizedLine.getTokenIterator() column = 0 currentWidth = 0 From abf306943ae0f43ee618b56d15d0e855ba420aea Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:49:54 +0200 Subject: [PATCH 09/22] :bug: Measure latin chars with subpixel font scaling --- spec/text-editor-component-spec.coffee | 4 ++-- src/display-buffer.coffee | 2 +- src/lines-component.coffee | 14 +++++--------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 9032598e8..6f7d3aa95 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2893,12 +2893,12 @@ describe "TextEditorComponent", -> describe "when changing the font", -> it "measures the default char width and the double width char width", -> - expect(editor.getDefaultCharWidth()).toBe(12) + expect(editor.getDefaultCharWidth()).toBeCloseTo(12, 0) component.setFontSize(10) nextAnimationFrame() - expect(editor.getDefaultCharWidth()).toBe(6) + expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0) expect(editor.getDoubleWidthCharWidth()).toBe(10) describe "hiding and showing the editor", -> diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 3d960e2d3..197d7858c 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -266,7 +266,7 @@ class DisplayBuffer extends Model @getEditorWidthInChars() getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> - lineMaxWidth = Math.round(@getSoftWrapColumn() * @getDefaultCharWidth()) + lineMaxWidth = @getSoftWrapColumn() * @getDefaultCharWidth() iterator = tokenizedLine.getTokenIterator() column = 0 currentWidth = 0 diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 8a97ccbfd..cce55b675 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -7,7 +7,9 @@ DummyLineNode.className = 'line' DummyLineNode.style.position = 'absolute' DummyLineNode.style.visibility = 'hidden' DummyLineNode.appendChild(document.createElement('span')) -DummyLineNode.firstChild.textContent = 'xフ' +DummyLineNode.appendChild(document.createElement('span')) +DummyLineNode.children[0].textContent = 'x' +DummyLineNode.children[1].textContent = '我' RangeForMeasurement = document.createRange() @@ -81,14 +83,8 @@ class LinesComponent extends TiledComponent textNode = DummyLineNode.firstChild.childNodes[0] lineHeightInPixels = DummyLineNode.getBoundingClientRect().height - - RangeForMeasurement.setStart(textNode, 0) - RangeForMeasurement.setEnd(textNode, 1) - defaultCharWidth = RangeForMeasurement.getBoundingClientRect().width - - RangeForMeasurement.setStart(textNode, 1) - RangeForMeasurement.setEnd(textNode, 2) - doubleWidthCharWidth = RangeForMeasurement.getBoundingClientRect().width + defaultCharWidth = DummyLineNode.children[0].getBoundingClientRect().width + doubleWidthCharWidth = DummyLineNode.children[1].getBoundingClientRect().width @domNode.removeChild(DummyLineNode) From 1fc9a25b5c824235edc8feac5946277d58d9b883 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:57:11 +0200 Subject: [PATCH 10/22] Recognize half width characters --- spec/text-utils-spec.coffee | 7 +++++++ src/text-utils.coffee | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index a8bdbc2f7..81df866f4 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -57,3 +57,10 @@ describe 'text utilities', -> expect(textUtils.isDoubleWidthCharacter(",")).toBe(true) expect(textUtils.isDoubleWidthCharacter("a")).toBe(false) + + describe ".isHalfWidthCharacter(character)", -> + it "returns true when the character is an half width form", -> + expect(textUtils.isHalfWidthCharacter("ハ")).toBe(true) + expect(textUtils.isHalfWidthCharacter("ヒ")).toBe(true) + expect(textUtils.isHalfWidthCharacter("ᆲ")).toBe(true) + expect(textUtils.isHalfWidthCharacter("B")).toBe(false) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index 1995094fe..3b0634948 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -81,6 +81,11 @@ isDoubleWidthCharacter = (character) -> isCjkUnifiedIdeograph(charCode) or isFullWidthForm(charCode) +isHalfWidthCharacter = (character) -> + charCode = character.charCodeAt(0) + + 0xFF65 <= charCode <= 0xFFDC + # Does the given string contain at least surrogate pair, variation sequence, # or combined character? # @@ -94,4 +99,4 @@ hasPairedCharacter = (string) -> index++ false -module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter} +module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter} From a3c3b48caa481615cffdb34383b930a469597dc9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 18:59:12 +0200 Subject: [PATCH 11/22] Recognize full width symbols --- spec/text-utils-spec.coffee | 1 + src/text-utils.coffee | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index 81df866f4..f95d7c22c 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -55,6 +55,7 @@ describe 'text utilities', -> expect(textUtils.isDoubleWidthCharacter("B")).toBe(true) expect(textUtils.isDoubleWidthCharacter(",")).toBe(true) + expect(textUtils.isDoubleWidthCharacter("¢")).toBe(true) expect(textUtils.isDoubleWidthCharacter("a")).toBe(false) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index 3b0634948..dfb834252 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -71,7 +71,8 @@ isCjkUnifiedIdeograph = (charCode) -> 0x4e00 <= charCode <= 0x9faf isFullWidthForm = (charCode) -> - 0xFF01 <= charCode <= 0xFF5E + 0xFF01 <= charCode <= 0xFF5E or + 0xFFE0 <= charCode <= 0xFFE6 isDoubleWidthCharacter = (character) -> charCode = character.charCodeAt(0) From 8f7b303fc5927811b876c73bccd066618bd4fbe1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 19:01:23 +0200 Subject: [PATCH 12/22] Recognize half width symbols --- spec/text-utils-spec.coffee | 1 + src/text-utils.coffee | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index f95d7c22c..eace87513 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -64,4 +64,5 @@ describe 'text utilities', -> expect(textUtils.isHalfWidthCharacter("ハ")).toBe(true) expect(textUtils.isHalfWidthCharacter("ヒ")).toBe(true) expect(textUtils.isHalfWidthCharacter("ᆲ")).toBe(true) + expect(textUtils.isHalfWidthCharacter("■")).toBe(true) expect(textUtils.isHalfWidthCharacter("B")).toBe(false) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index dfb834252..cfe0e807c 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -65,10 +65,10 @@ isKoreanCharacter = (charCode) -> 0xD7B0 <= charCode <= 0xD7FF isJapaneseCharacter = (charCode) -> - 0x3000 <= charCode <= 0x30ff + 0x3000 <= charCode <= 0x30FF isCjkUnifiedIdeograph = (charCode) -> - 0x4e00 <= charCode <= 0x9faf + 0x4E00 <= charCode <= 0x9FAF isFullWidthForm = (charCode) -> 0xFF01 <= charCode <= 0xFF5E or @@ -85,7 +85,8 @@ isDoubleWidthCharacter = (character) -> isHalfWidthCharacter = (character) -> charCode = character.charCodeAt(0) - 0xFF65 <= charCode <= 0xFFDC + 0xFF65 <= charCode <= 0xFFDC or + 0xFFE8 <= charCode <= 0xFFEE # Does the given string contain at least surrogate pair, variation sequence, # or combined character? From 91e9f16ff691d1b380b95537cd993f7e05948ce1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 19:22:12 +0200 Subject: [PATCH 13/22] :green_heart: --- src/display-buffer.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 197d7858c..2becff4e4 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -195,7 +195,7 @@ class DisplayBuffer extends Model if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth - @updateWrappedScreenLines() if @isSoftWrapped() and @getWidth()? + @updateWrappedScreenLines() if @isSoftWrapped() and @getEditorWidthInChars()? defaultCharWidth getCursorWidth: -> 1 From c616e86a00374ab56fcc1a379e48b3a02ea9c95a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 19:23:50 +0200 Subject: [PATCH 14/22] Take into account half width chars as well --- spec/display-buffer-spec.coffee | 9 +++++++++ src/display-buffer.coffee | 11 ++++++++++- src/text-editor.coffee | 6 ++++-- src/token-iterator.coffee | 5 ++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index aafca68fe..ad1dba691 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -70,6 +70,15 @@ describe "DisplayBuffer", -> expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为우리포럼hello ") expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("world this is a pretty long latin line") + describe "when there are half width characters", -> + it "takes them into account when finding the soft wrap column", -> + displayBuffer.setDefaultCharWidth(1, 0, 5) + buffer.setText("abcᆰᆱᆲネヌネノハヒフヒフヌᄡ○○○hello world this is a pretty long line") + + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("abcᆰᆱᆲネヌネノハヒ") + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("フヒフヌᄡ○○○hello ") + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("world this is a pretty long line") + describe "when editor.softWrapAtPreferredLineLength is set", -> it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", -> atom.config.set('editor.preferredLineLength', 100) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 2becff4e4..50e4741c8 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -189,12 +189,19 @@ class DisplayBuffer extends Model getLineHeightInPixels: -> @lineHeightInPixels setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels + getHalfWidthCharWidth: -> @halfWidthCharWidth + getDoubleWidthCharWidth: -> @doubleWidthCharWidth + getDefaultCharWidth: -> @defaultCharWidth - setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth=defaultCharWidth) -> + + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) -> + doubleWidthCharWidth ?= defaultCharWidth + halfWidthCharWidth ?= defaultCharWidth if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth + @halfWidthCharWidth = halfWidthCharWidth @updateWrappedScreenLines() if @isSoftWrapped() and @getEditorWidthInChars()? defaultCharWidth @@ -281,6 +288,8 @@ class DisplayBuffer extends Model if iterator.hasDoubleWidthCharacterAt(textIndex) charWidth = @getDoubleWidthCharWidth() + else if iterator.hasHalfWidthCharacterAt(textIndex) + charWidth = @getHalfWidthCharWidth() else charWidth = @getDefaultCharWidth() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e4b0c8a37..58d590687 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2976,11 +2976,13 @@ class TextEditor extends Model getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) + getHalfWidthCharWidth: -> @displayBuffer.getHalfWidthCharWidth() + getDoubleWidthCharWidth: -> @displayBuffer.getDoubleWidthCharWidth() getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth() - setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth=defaultCharWidth) -> - @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth) + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) -> + @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) setHeight: (height, reentrant=false) -> if reentrant diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index 4c4686635..9a5b63892 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -1,5 +1,5 @@ {SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' -{isDoubleWidthCharacter} = require './text-utils' +{isDoubleWidthCharacter, isHalfWidthCharacter} = require './text-utils' module.exports = class TokenIterator @@ -86,5 +86,8 @@ class TokenIterator hasDoubleWidthCharacterAt: (charIndex) -> isDoubleWidthCharacter(@getText()[charIndex]) + hasHalfWidthCharacterAt: (charIndex) -> + isHalfWidthCharacter(@getText()[charIndex]) + isAtomic: -> @isSoftTab() or @isHardTab() or @isSoftWrapIndentation() or @isPairedCharacter() From 7394cc531cb4c029cb1980e93a14a3aba9c1230e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 20:06:38 +0200 Subject: [PATCH 15/22] Measure half width characters --- spec/text-editor-component-spec.coffee | 1 + src/lines-component.coffee | 5 ++++- src/text-editor-presenter.coffee | 7 ++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 6f7d3aa95..a110f9347 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2900,6 +2900,7 @@ describe "TextEditorComponent", -> expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0) expect(editor.getDoubleWidthCharWidth()).toBe(10) + expect(editor.getHalfWidthCharWidth()).toBe(5) describe "hiding and showing the editor", -> describe "when the editor is hidden when it is mounted", -> diff --git a/src/lines-component.coffee b/src/lines-component.coffee index cce55b675..031a790f8 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -8,8 +8,10 @@ DummyLineNode.style.position = 'absolute' DummyLineNode.style.visibility = 'hidden' DummyLineNode.appendChild(document.createElement('span')) DummyLineNode.appendChild(document.createElement('span')) +DummyLineNode.appendChild(document.createElement('span')) DummyLineNode.children[0].textContent = 'x' DummyLineNode.children[1].textContent = '我' +DummyLineNode.children[2].textContent = 'ハ' RangeForMeasurement = document.createRange() @@ -85,11 +87,12 @@ class LinesComponent extends TiledComponent lineHeightInPixels = DummyLineNode.getBoundingClientRect().height defaultCharWidth = DummyLineNode.children[0].getBoundingClientRect().width doubleWidthCharWidth = DummyLineNode.children[1].getBoundingClientRect().width + halfWidthCharWidth = DummyLineNode.children[2].getBoundingClientRect().width @domNode.removeChild(DummyLineNode) @presenter.setLineHeight(lineHeightInPixels) - @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth) + @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) lineNodeForLineIdAndScreenRow: (lineId, screenRow) -> tile = @presenter.tileForRow(screenRow) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 92ccb2d56..c7f260b79 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1122,11 +1122,12 @@ class TextEditorPresenter @mouseWheelScreenRow = screenRow @didStartScrolling() - setBaseCharacterWidth: (baseCharacterWidth, doubleWidthCharWidth) -> - unless @baseCharacterWidth is baseCharacterWidth and @doubleWidthCharWidth is doubleWidthCharWidth + setBaseCharacterWidth: (baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth) -> + unless @baseCharacterWidth is baseCharacterWidth and @doubleWidthCharWidth is doubleWidthCharWidth and @halfWidthCharWidth is halfWidthCharWidth @baseCharacterWidth = baseCharacterWidth @doubleWidthCharWidth = doubleWidthCharWidth - @model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth) + @halfWidthCharWidth = halfWidthCharWidth + @model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth) @characterWidthsChanged() characterWidthsChanged: -> From c9eb75cbe1f68228187d4afa7b798aff24f80b48 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 20:14:38 +0200 Subject: [PATCH 16/22] :memo: Better wording on specs --- spec/text-editor-component-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index a110f9347..ab87e00a8 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2892,7 +2892,7 @@ describe "TextEditorComponent", -> expect(event.abortKeyBinding).toHaveBeenCalled() describe "when changing the font", -> - it "measures the default char width and the double width char width", -> + it "measures the default char, the double width char and the half width char widths ", -> expect(editor.getDefaultCharWidth()).toBeCloseTo(12, 0) component.setFontSize(10) From 4766c98bbe66422fadfaa23fb160411dfc66eb11 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 15 Oct 2015 21:02:21 +0200 Subject: [PATCH 17/22] Treat Korean characters as different kinds of chars --- spec/display-buffer-spec.coffee | 11 +++++++++++ spec/text-editor-component-spec.coffee | 3 ++- spec/text-utils-spec.coffee | 14 +++++++++++--- src/display-buffer.coffee | 10 ++++++++-- src/lines-component.coffee | 5 ++++- src/text-editor-presenter.coffee | 7 ++++--- src/text-editor.coffee | 6 ++++-- src/text-utils.coffee | 19 ++++++++++--------- src/token-iterator.coffee | 5 ++++- 9 files changed, 58 insertions(+), 22 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index ad1dba691..19eccd58d 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -79,6 +79,17 @@ describe "DisplayBuffer", -> expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("フヒフヌᄡ○○○hello ") expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("world this is a pretty long line") + describe "when there are korean characters", -> + it "takes them into account when finding the soft wrap column", -> + displayBuffer.setDefaultCharWidth(1, 0, 0, 10) + buffer.setText("1234세계를 향한 대화, 유니코 제10회유니코드국제") + + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("1234세계를 ") + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("향한 대화, ") + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("유니코 ") + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("제10회유니") + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe("코드국제") + describe "when editor.softWrapAtPreferredLineLength is set", -> it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", -> atom.config.set('editor.preferredLineLength', 100) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index ab87e00a8..45e1dbb6b 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2892,13 +2892,14 @@ describe "TextEditorComponent", -> expect(event.abortKeyBinding).toHaveBeenCalled() describe "when changing the font", -> - it "measures the default char, the double width char and the half width char widths ", -> + it "measures the default char, the korean char, the double width char and the half width char widths", -> expect(editor.getDefaultCharWidth()).toBeCloseTo(12, 0) component.setFontSize(10) nextAnimationFrame() expect(editor.getDefaultCharWidth()).toBeCloseTo(6, 0) + expect(editor.getKoreanCharWidth()).toBeCloseTo(9, 0) expect(editor.getDoubleWidthCharWidth()).toBe(10) expect(editor.getHalfWidthCharWidth()).toBe(5) diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index eace87513..dd528b37e 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -46,13 +46,11 @@ describe 'text utilities', -> expect(textUtils.isPairedCharacter('ae\u0301c', 4)).toBe false describe ".isDoubleWidthCharacter(character)", -> - it "returns true when the character is either japanese, korean, chinese or a full width form", -> + it "returns true when the character is either japanese, chinese or a full width form", -> expect(textUtils.isDoubleWidthCharacter("我")).toBe(true) expect(textUtils.isDoubleWidthCharacter("私")).toBe(true) - expect(textUtils.isDoubleWidthCharacter("우")).toBe(true) - expect(textUtils.isDoubleWidthCharacter("B")).toBe(true) expect(textUtils.isDoubleWidthCharacter(",")).toBe(true) expect(textUtils.isDoubleWidthCharacter("¢")).toBe(true) @@ -65,4 +63,14 @@ describe 'text utilities', -> expect(textUtils.isHalfWidthCharacter("ヒ")).toBe(true) expect(textUtils.isHalfWidthCharacter("ᆲ")).toBe(true) expect(textUtils.isHalfWidthCharacter("■")).toBe(true) + expect(textUtils.isHalfWidthCharacter("B")).toBe(false) + + describe ".isKoreanCharacter(character)", -> + it "returns true when the character is a korean character", -> + expect(textUtils.isKoreanCharacter("우")).toBe(true) + expect(textUtils.isKoreanCharacter("가")).toBe(true) + expect(textUtils.isKoreanCharacter("ㅢ")).toBe(true) + expect(textUtils.isKoreanCharacter("ㄼ")).toBe(true) + + expect(textUtils.isKoreanCharacter("O")).toBe(false) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 50e4741c8..2a93aa951 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -189,19 +189,23 @@ class DisplayBuffer extends Model getLineHeightInPixels: -> @lineHeightInPixels setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels + getKoreanCharWidth: -> @koreanCharWidth + getHalfWidthCharWidth: -> @halfWidthCharWidth getDoubleWidthCharWidth: -> @doubleWidthCharWidth getDefaultCharWidth: -> @defaultCharWidth - setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) -> + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) -> doubleWidthCharWidth ?= defaultCharWidth halfWidthCharWidth ?= defaultCharWidth - if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth + koreanCharWidth ?= defaultCharWidth + if defaultCharWidth isnt @defaultCharWidth or doubleWidthCharWidth isnt @doubleWidthCharWidth and halfWidthCharWidth isnt @halfWidthCharWidth and koreanCharWidth isnt @koreanCharWidth @defaultCharWidth = defaultCharWidth @doubleWidthCharWidth = doubleWidthCharWidth @halfWidthCharWidth = halfWidthCharWidth + @koreanCharWidth = koreanCharWidth @updateWrappedScreenLines() if @isSoftWrapped() and @getEditorWidthInChars()? defaultCharWidth @@ -290,6 +294,8 @@ class DisplayBuffer extends Model charWidth = @getDoubleWidthCharWidth() else if iterator.hasHalfWidthCharacterAt(textIndex) charWidth = @getHalfWidthCharWidth() + else if iterator.hasKoreanCharacterAt(textIndex) + charWidth = @getKoreanCharWidth() else charWidth = @getDefaultCharWidth() diff --git a/src/lines-component.coffee b/src/lines-component.coffee index 031a790f8..98b274fae 100644 --- a/src/lines-component.coffee +++ b/src/lines-component.coffee @@ -9,9 +9,11 @@ DummyLineNode.style.visibility = 'hidden' DummyLineNode.appendChild(document.createElement('span')) DummyLineNode.appendChild(document.createElement('span')) DummyLineNode.appendChild(document.createElement('span')) +DummyLineNode.appendChild(document.createElement('span')) DummyLineNode.children[0].textContent = 'x' DummyLineNode.children[1].textContent = '我' DummyLineNode.children[2].textContent = 'ハ' +DummyLineNode.children[3].textContent = '세' RangeForMeasurement = document.createRange() @@ -88,11 +90,12 @@ class LinesComponent extends TiledComponent defaultCharWidth = DummyLineNode.children[0].getBoundingClientRect().width doubleWidthCharWidth = DummyLineNode.children[1].getBoundingClientRect().width halfWidthCharWidth = DummyLineNode.children[2].getBoundingClientRect().width + koreanCharWidth = DummyLineNode.children[3].getBoundingClientRect().width @domNode.removeChild(DummyLineNode) @presenter.setLineHeight(lineHeightInPixels) - @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) + @presenter.setBaseCharacterWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) lineNodeForLineIdAndScreenRow: (lineId, screenRow) -> tile = @presenter.tileForRow(screenRow) diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index c7f260b79..985ca1c95 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -1122,12 +1122,13 @@ class TextEditorPresenter @mouseWheelScreenRow = screenRow @didStartScrolling() - setBaseCharacterWidth: (baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth) -> - unless @baseCharacterWidth is baseCharacterWidth and @doubleWidthCharWidth is doubleWidthCharWidth and @halfWidthCharWidth is halfWidthCharWidth + setBaseCharacterWidth: (baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) -> + unless @baseCharacterWidth is baseCharacterWidth and @doubleWidthCharWidth is doubleWidthCharWidth and @halfWidthCharWidth is halfWidthCharWidth and koreanCharWidth is @koreanCharWidth @baseCharacterWidth = baseCharacterWidth @doubleWidthCharWidth = doubleWidthCharWidth @halfWidthCharWidth = halfWidthCharWidth - @model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth) + @koreanCharWidth = koreanCharWidth + @model.setDefaultCharWidth(baseCharacterWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) @characterWidthsChanged() characterWidthsChanged: -> diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 58d590687..2610c5970 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2976,13 +2976,15 @@ class TextEditor extends Model getLineHeightInPixels: -> @displayBuffer.getLineHeightInPixels() setLineHeightInPixels: (lineHeightInPixels) -> @displayBuffer.setLineHeightInPixels(lineHeightInPixels) + getKoreanCharWidth: -> @displayBuffer.getKoreanCharWidth() + getHalfWidthCharWidth: -> @displayBuffer.getHalfWidthCharWidth() getDoubleWidthCharWidth: -> @displayBuffer.getDoubleWidthCharWidth() getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth() - setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) -> - @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth) + setDefaultCharWidth: (defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) -> + @displayBuffer.setDefaultCharWidth(defaultCharWidth, doubleWidthCharWidth, halfWidthCharWidth, koreanCharWidth) setHeight: (height, reentrant=false) -> if reentrant diff --git a/src/text-utils.coffee b/src/text-utils.coffee index cfe0e807c..82bed4da5 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -57,13 +57,6 @@ isPairedCharacter = (string, index=0) -> isVariationSequence(charCodeA, charCodeB) or isCombinedCharacter(charCodeA, charCodeB) -isKoreanCharacter = (charCode) -> - 0xAC00 <= charCode <= 0xD7A3 or - 0x1100 <= charCode <= 0x11FF or - 0x3130 <= charCode <= 0x318F or - 0xA960 <= charCode <= 0xA97F or - 0xD7B0 <= charCode <= 0xD7FF - isJapaneseCharacter = (charCode) -> 0x3000 <= charCode <= 0x30FF @@ -77,7 +70,6 @@ isFullWidthForm = (charCode) -> isDoubleWidthCharacter = (character) -> charCode = character.charCodeAt(0) - isKoreanCharacter(charCode) or isJapaneseCharacter(charCode) or isCjkUnifiedIdeograph(charCode) or isFullWidthForm(charCode) @@ -88,6 +80,15 @@ isHalfWidthCharacter = (character) -> 0xFF65 <= charCode <= 0xFFDC or 0xFFE8 <= charCode <= 0xFFEE +isKoreanCharacter = (character) -> + charCode = character.charCodeAt(0) + + 0xAC00 <= charCode <= 0xD7A3 or + 0x1100 <= charCode <= 0x11FF or + 0x3130 <= charCode <= 0x318F or + 0xA960 <= charCode <= 0xA97F or + 0xD7B0 <= charCode <= 0xD7FF + # Does the given string contain at least surrogate pair, variation sequence, # or combined character? # @@ -101,4 +102,4 @@ hasPairedCharacter = (string) -> index++ false -module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter} +module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index 9a5b63892..120f62fe4 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -1,5 +1,5 @@ {SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' -{isDoubleWidthCharacter, isHalfWidthCharacter} = require './text-utils' +{isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} = require './text-utils' module.exports = class TokenIterator @@ -89,5 +89,8 @@ class TokenIterator hasHalfWidthCharacterAt: (charIndex) -> isHalfWidthCharacter(@getText()[charIndex]) + hasKoreanCharacterAt: (charIndex) -> + isKoreanCharacter(@getText()[charIndex]) + isAtomic: -> @isSoftTab() or @isHardTab() or @isSoftWrapIndentation() or @isPairedCharacter() From eb3133b7e7c351e53f377b1d2d46162e361d095d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 16 Oct 2015 10:30:45 +0200 Subject: [PATCH 18/22] :green_heart: --- spec/display-buffer-spec.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 3e1178749..68dd9c754 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -68,17 +68,17 @@ describe "DisplayBuffer", -> describe "rendering of soft-wrapped lines", -> describe "when there are double width characters", -> it "takes them into account when finding the soft wrap column", -> - buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为우리포럼hello world this is a pretty long latin line") - displayBuffer.setDefaultCharWidth(1, 5) + buffer.setText("私たちのフ是一个地方,数千名学生12345业余爱们的板作为hello world this is a pretty long latin line") + displayBuffer.setDefaultCharWidth(1, 5, 0, 0) expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("私たちのフ是一个地方") expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe(",数千名学生12345业余爱") - expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为우리포럼hello ") - expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("world this is a pretty long latin line") + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("们的板作为hello world this is a ") + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("pretty long latin line") describe "when there are half width characters", -> it "takes them into account when finding the soft wrap column", -> - displayBuffer.setDefaultCharWidth(1, 0, 5) + displayBuffer.setDefaultCharWidth(1, 0, 5, 0) buffer.setText("abcᆰᆱᆲネヌネノハヒフヒフヌᄡ○○○hello world this is a pretty long line") expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("abcᆰᆱᆲネヌネノハヒ") From 3f53a726209a9c280606b536af2037d533b7fd82 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 16 Oct 2015 10:38:51 +0200 Subject: [PATCH 19/22] :racehorse: Cache text in TokenIterator --- src/token-iterator.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index 92529d0e9..de914e4b5 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -53,6 +53,8 @@ class TokenIterator else @screenEnd = @screenStart + tag @bufferEnd = @bufferStart + tag + + @text = @line.text.substring(@screenStart, @screenEnd) return true false @@ -68,8 +70,7 @@ class TokenIterator getScopes: -> @scopes - getText: -> - @line.text.substring(@screenStart, @screenEnd) + getText: -> @text isSoftTab: -> @line.specialTokens[@index] is SoftTab From e843c2f05821a58e7a38396b1d81998406d3b309 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 16 Oct 2015 10:44:18 +0200 Subject: [PATCH 20/22] :racehorse: Fetch scopes only if required --- src/display-buffer.coffee | 2 +- src/lines-yardstick.coffee | 4 ++-- src/token-iterator.coffee | 45 +++++++++++++++++++++++--------------- src/tokenized-line.coffee | 2 +- 4 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index b3a5a628c..eccdfb7f3 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -293,7 +293,7 @@ class DisplayBuffer extends Model getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> lineMaxWidth = @getSoftWrapColumn() * @getDefaultCharWidth() - iterator = tokenizedLine.getTokenIterator() + iterator = tokenizedLine.getTokenIterator(false) column = 0 currentWidth = 0 while iterator.next() diff --git a/src/lines-yardstick.coffee b/src/lines-yardstick.coffee index 3254451f1..fa00bae40 100644 --- a/src/lines-yardstick.coffee +++ b/src/lines-yardstick.coffee @@ -40,7 +40,7 @@ class LinesYardstick previousColumn = 0 previousLeft = 0 - @tokenIterator.reset(line) + @tokenIterator.reset(line, false) while @tokenIterator.next() text = @tokenIterator.getText() textIndex = 0 @@ -112,7 +112,7 @@ class LinesYardstick indexWithinTextNode = null charIndex = 0 - @tokenIterator.reset(line) + @tokenIterator.reset(line, false) while @tokenIterator.next() break if foundIndexWithinTextNode? diff --git a/src/token-iterator.coffee b/src/token-iterator.coffee index de914e4b5..8f0fe202f 100644 --- a/src/token-iterator.coffee +++ b/src/token-iterator.coffee @@ -3,18 +3,16 @@ module.exports = class TokenIterator - constructor: ({@grammarRegistry}, line) -> - @reset(line) if line? + constructor: ({@grammarRegistry}, line, enableScopes) -> + @reset(line, enableScopes) if line? - reset: (@line) -> + reset: (@line, @enableScopes=true) -> @index = null @bufferStart = @line.startBufferColumn @bufferEnd = @bufferStart @screenStart = 0 @screenEnd = 0 - @scopes = @line.openScopes.map (id) => @grammarRegistry.scopeForId(id) - @scopeStarts = @scopes.slice() - @scopeEnds = [] + @resetScopes() if @enableScopes this next: -> @@ -22,26 +20,16 @@ class TokenIterator if @index? @index++ - @scopeEnds.length = 0 - @scopeStarts.length = 0 @bufferStart = @bufferEnd @screenStart = @screenEnd + @clearScopeStartsAndEnds() if @enableScopes else @index = 0 while @index < tags.length tag = tags[@index] if tag < 0 - scope = @grammarRegistry.scopeForId(tag) - if tag % 2 is 0 - if @scopeStarts[@scopeStarts.length - 1] is scope - @scopeStarts.pop() - else - @scopeEnds.push(scope) - @scopes.pop() - else - @scopeStarts.push(scope) - @scopes.push(scope) + @handleScopeForTag(tag) if @enableScopes @index++ else if @isHardTab() @@ -59,6 +47,27 @@ class TokenIterator false + resetScopes: -> + @scopes = @line.openScopes.map (id) => @grammarRegistry.scopeForId(id) + @scopeStarts = @scopes.slice() + @scopeEnds = [] + + clearScopeStartsAndEnds: -> + @scopeEnds.length = 0 + @scopeStarts.length = 0 + + handleScopeForTag: (tag) -> + scope = @grammarRegistry.scopeForId(tag) + if tag % 2 is 0 + if @scopeStarts[@scopeStarts.length - 1] is scope + @scopeStarts.pop() + else + @scopeEnds.push(scope) + @scopes.pop() + else + @scopeStarts.push(scope) + @scopes.push(scope) + getBufferStart: -> @bufferStart getBufferEnd: -> @bufferEnd diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index bf234b6d3..2a27d3f12 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -184,7 +184,7 @@ class TokenizedLine @lineIsWhitespaceOnly = true @firstTrailingWhitespaceIndex = 0 - getTokenIterator: -> @tokenIterator.reset(this) + getTokenIterator: -> @tokenIterator.reset(this, arguments...) Object.defineProperty @prototype, 'tokens', get: -> iterator = @getTokenIterator() From 05c6c9f9be9bc2d591d2ab87db8e9f8e2e0c21c4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 16 Oct 2015 11:14:20 +0200 Subject: [PATCH 21/22] Soft wrap only when we know lineMaxWidth --- src/display-buffer.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index eccdfb7f3..1021955cf 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -293,6 +293,10 @@ class DisplayBuffer extends Model getSoftWrapColumnForTokenizedLine: (tokenizedLine) -> lineMaxWidth = @getSoftWrapColumn() * @getDefaultCharWidth() + + return if Number.isNaN(lineMaxWidth) + return 0 if lineMaxWidth is 0 + iterator = tokenizedLine.getTokenIterator(false) column = 0 currentWidth = 0 From cd47c7be02e497ba9fb4a95dbbe3c1a7891b3790 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 16 Oct 2015 11:22:03 +0200 Subject: [PATCH 22/22] Fix leftover spec --- spec/text-editor-spec.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index dd01b816f..8ce026741 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1531,6 +1531,7 @@ describe "TextEditor", -> it "can add selections to soft-wrapped line segments", -> editor.setSoftWrapped(true) editor.setEditorWidthInChars(40) + editor.setDefaultCharWidth(1) editor.setSelectedScreenRange([[3, 10], [3, 15]]) editor.addSelectionBelow()