diff --git a/package.json b/package.json index 3dfc86181..c04aa87df 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", "find-and-replace": "0.195.0", - "fuzzy-finder": "0.93.0", + "fuzzy-finder": "0.94.0", "git-diff": "0.57.0", "go-to-line": "0.30.0", "grammar-selector": "0.48.0", diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index a54c01198..8c4adca44 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -88,13 +88,13 @@ describe "DisplayBuffer", -> 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회유니코드국제") + 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("코드국제") + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe("1234세계를향") + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe("한대화,유") + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe("니코제10회") + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe("유니코드국") + 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", -> @@ -130,8 +130,25 @@ describe "DisplayBuffer", -> expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe ' sort(left).concat(pivot).concat(sort(right));' + it "wraps the line at the first CJK character before the boundary", -> + displayBuffer.setEditorWidthInChars(10) + + buffer.setTextInRange([[0, 0], [1, 0]], 'abcd efg유私フ业余爱\n') + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcd efg유私' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'フ业余爱' + + buffer.setTextInRange([[0, 0], [1, 0]], 'abcd ef유gef业余爱\n') + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcd ef유' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'gef业余爱' + describe "when there is no whitespace before the boundary", -> - it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", -> + it "wraps the line at the first CJK character before the boundary", -> + buffer.setTextInRange([[0, 0], [1, 0]], '私たちのabcdefghij\n') + displayBuffer.setEditorWidthInChars(10) + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe '私たちの' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'abcdefghij' + + it "wraps the line exactly at the boundary when no CJK character is found, since there's no more graceful place to wrap it", -> buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n') displayBuffer.setEditorWidthInChars(10) expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcdefghij' diff --git a/spec/text-utils-spec.coffee b/spec/text-utils-spec.coffee index dd528b37e..aa36c5003 100644 --- a/spec/text-utils-spec.coffee +++ b/spec/text-utils-spec.coffee @@ -74,3 +74,23 @@ describe 'text utilities', -> expect(textUtils.isKoreanCharacter("ㄼ")).toBe(true) expect(textUtils.isKoreanCharacter("O")).toBe(false) + + describe ".isCJKCharacter(character)", -> + it "returns true when the character is either a korean, half-width or double-width character", -> + expect(textUtils.isCJKCharacter("我")).toBe(true) + expect(textUtils.isCJKCharacter("私")).toBe(true) + expect(textUtils.isCJKCharacter("B")).toBe(true) + expect(textUtils.isCJKCharacter(",")).toBe(true) + expect(textUtils.isCJKCharacter("¢")).toBe(true) + expect(textUtils.isCJKCharacter("ハ")).toBe(true) + expect(textUtils.isCJKCharacter("ヒ")).toBe(true) + expect(textUtils.isCJKCharacter("ᆲ")).toBe(true) + expect(textUtils.isCJKCharacter("■")).toBe(true) + expect(textUtils.isCJKCharacter("우")).toBe(true) + expect(textUtils.isCJKCharacter("가")).toBe(true) + expect(textUtils.isCJKCharacter("ㅢ")).toBe(true) + expect(textUtils.isCJKCharacter("ㄼ")).toBe(true) + + expect(textUtils.isDoubleWidthCharacter("a")).toBe(false) + expect(textUtils.isDoubleWidthCharacter("O")).toBe(false) + expect(textUtils.isDoubleWidthCharacter("z")).toBe(false) diff --git a/src/text-utils.coffee b/src/text-utils.coffee index 82bed4da5..af17335aa 100644 --- a/src/text-utils.coffee +++ b/src/text-utils.coffee @@ -57,11 +57,11 @@ isPairedCharacter = (string, index=0) -> isVariationSequence(charCodeA, charCodeB) or isCombinedCharacter(charCodeA, charCodeB) -isJapaneseCharacter = (charCode) -> +IsJapaneseKanaCharacter = (charCode) -> 0x3000 <= charCode <= 0x30FF -isCjkUnifiedIdeograph = (charCode) -> - 0x4E00 <= charCode <= 0x9FAF +isCJKUnifiedIdeograph = (charCode) -> + 0x4E00 <= charCode <= 0x9FFF isFullWidthForm = (charCode) -> 0xFF01 <= charCode <= 0xFF5E or @@ -70,8 +70,8 @@ isFullWidthForm = (charCode) -> isDoubleWidthCharacter = (character) -> charCode = character.charCodeAt(0) - isJapaneseCharacter(charCode) or - isCjkUnifiedIdeograph(charCode) or + IsJapaneseKanaCharacter(charCode) or + isCJKUnifiedIdeograph(charCode) or isFullWidthForm(charCode) isHalfWidthCharacter = (character) -> @@ -89,6 +89,11 @@ isKoreanCharacter = (character) -> 0xA960 <= charCode <= 0xA97F or 0xD7B0 <= charCode <= 0xD7FF +isCJKCharacter = (character) -> + isDoubleWidthCharacter(character) or + isHalfWidthCharacter(character) or + isKoreanCharacter(character) + # Does the given string contain at least surrogate pair, variation sequence, # or combined character? # @@ -102,4 +107,4 @@ hasPairedCharacter = (string) -> index++ false -module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter} +module.exports = {isPairedCharacter, hasPairedCharacter, isDoubleWidthCharacter, isHalfWidthCharacter, isKoreanCharacter, isCJKCharacter} diff --git a/src/tokenized-line.coffee b/src/tokenized-line.coffee index 2a27d3f12..c97a621ac 100644 --- a/src/tokenized-line.coffee +++ b/src/tokenized-line.coffee @@ -1,5 +1,5 @@ _ = require 'underscore-plus' -{isPairedCharacter} = require './text-utils' +{isPairedCharacter, isCJKCharacter} = require './text-utils' Token = require './token' {SoftTab, HardTab, PairedCharacter, SoftWrapIndent} = require './special-token-symbols' @@ -322,15 +322,18 @@ class TokenizedLine return unless @text.length > maxColumn if /\s/.test(@text[maxColumn]) - # search forward for the start of a word past the boundary + # search forward for the start of a word past the boundary for column in [maxColumn..@text.length] return column if /\S/.test(@text[column]) return @text.length + else if isCJKCharacter(@text[maxColumn]) + maxColumn else # search backward for the start of the word on the boundary for column in [maxColumn..@firstNonWhitespaceIndex] - return column + 1 if /\s/.test(@text[column]) + if /\s/.test(@text[column]) or isCJKCharacter(@text[column]) + return column + 1 return maxColumn