mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-13 08:44:12 +03:00
WIP: Add a LineWrapper object
Only passing specs are focused. Everything is still broken. Editor uses the line wrapper to render lines, but the line wrapper isn't updating on buffer change events yet. Still moving vertically and clipping positions based on unwrapped lines as well.
This commit is contained in:
parent
62470f61ab
commit
b21595f037
@ -50,15 +50,21 @@ describe "Editor", ->
|
||||
buffer.insert([1,0], "/*")
|
||||
expect(editor.lines.find('.line:eq(2) span:eq(0)')).toMatchSelector '.comment'
|
||||
|
||||
describe "when soft-wrap is enabled", ->
|
||||
fdescribe "when soft-wrap is enabled", ->
|
||||
beforeEach ->
|
||||
editor.attachToDom()
|
||||
editor.width(editor.charWidth * 50)
|
||||
editor.setSoftWrap(true)
|
||||
|
||||
it "wraps lines that are too long to fit within the editor's width", ->
|
||||
expect(editor.lines.find('pre:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [],"
|
||||
expect(editor.lines.find('pre:eq(4)').text()).toBe " right = [];"
|
||||
it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", ->
|
||||
expect(editor.lines.find('pre:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [], "
|
||||
expect(editor.lines.find('pre:eq(4)').text()).toBe "right = [];"
|
||||
|
||||
editor.cursor.setPosition([3, 52])
|
||||
expect(editor.cursor.position()).toEqual(editor.lines.find('pre:eq(4)').position())
|
||||
|
||||
editor.cursor.setPosition([4, 0])
|
||||
expect(editor.cursor.position()).toEqual(editor.lines.find('pre:eq(5)').position())
|
||||
|
||||
describe "cursor movement", ->
|
||||
describe ".setCursorPosition({row, column})", ->
|
||||
|
51
spec/atom/line-wrapper-spec.coffee
Normal file
51
spec/atom/line-wrapper-spec.coffee
Normal file
@ -0,0 +1,51 @@
|
||||
Buffer = require 'buffer'
|
||||
LineWrapper = require 'line-wrapper'
|
||||
Highlighter = require 'highlighter'
|
||||
_ = require 'underscore'
|
||||
|
||||
fdescribe "LineWrapper", ->
|
||||
[wrapper, buffer] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = new Buffer(require.resolve('fixtures/sample.js'))
|
||||
wrapper = new LineWrapper(50, new Highlighter(buffer))
|
||||
|
||||
describe ".segmentsForRow(row)", ->
|
||||
describe "when the line does not need to wrap", ->
|
||||
it "returns tokens for a single segment", ->
|
||||
line = buffer.getLine(0)
|
||||
expect(line.length).toBeLessThan(50)
|
||||
segments = wrapper.segmentsForRow(0)
|
||||
expect(segments.length).toBe 1
|
||||
expect(segments[0].lastIndex).toBe line.length
|
||||
|
||||
describe "when the line needs to wrap once", ->
|
||||
it "breaks the line into 2 segments at the beginning of the first word that exceeds the max length", ->
|
||||
line = buffer.getLine(6)
|
||||
expect(line.length).toBeGreaterThan 50
|
||||
segments = wrapper.segmentsForRow(6)
|
||||
expect(segments.length).toBe 2
|
||||
expect(segments[0].lastIndex).toBe 45
|
||||
expect(segments[0].map((t) -> t.value).join('')).toBe ' current < pivot ? left.push(current) : '
|
||||
|
||||
expect(segments[1].lastIndex).toBe 65
|
||||
expect(segments[1].map((t) -> t.value).join('')).toBe 'right.push(current);'
|
||||
|
||||
describe "when the line needs to wrap more than once", ->
|
||||
it "breaks the line into multiple segments", ->
|
||||
wrapper.setMaxLength(30)
|
||||
segments = wrapper.segmentsForRow(6)
|
||||
|
||||
expect(segments.length).toBe 3
|
||||
|
||||
expect(segments[0].lastIndex).toBe 24
|
||||
expect(_.pluck(segments[0], 'value').join('')).toBe ' current < pivot ? '
|
||||
|
||||
expect(segments[1].lastIndex).toBe 45
|
||||
expect(_.pluck(segments[1], 'value').join('')).toBe 'left.push(current) : '
|
||||
|
||||
expect(segments[2].lastIndex).toBe 65
|
||||
expect(_.pluck(segments[2], 'value').join('')).toBe 'right.push(current);'
|
||||
|
||||
describe "when the buffer changes", ->
|
||||
|
@ -4,6 +4,7 @@ Point = require 'point'
|
||||
Cursor = require 'cursor'
|
||||
Selection = require 'selection'
|
||||
Highlighter = require 'highlighter'
|
||||
LineWrapper = require 'line-wrapper'
|
||||
UndoManager = require 'undo-manager'
|
||||
Range = require 'range'
|
||||
|
||||
@ -20,12 +21,14 @@ class Editor extends View
|
||||
vScrollMargin: 2
|
||||
hScrollMargin: 10
|
||||
softWrap: false
|
||||
cursor: null
|
||||
buffer: null
|
||||
undoManager: null
|
||||
selection: null
|
||||
lineHeight: null
|
||||
charWidth: null
|
||||
cursor: null
|
||||
selection: null
|
||||
buffer: null
|
||||
highlighter: null
|
||||
lineWrapper: null
|
||||
undoManager: null
|
||||
|
||||
initialize: () ->
|
||||
requireStylesheet 'editor.css'
|
||||
@ -111,26 +114,8 @@ class Editor extends View
|
||||
@on 'mousemove', moveHandler
|
||||
$(document).one 'mouseup', => @off 'mousemove', moveHandler
|
||||
|
||||
|
||||
buildLineElement: (row) ->
|
||||
maxSegmentLength =
|
||||
if @softWrap
|
||||
Math.floor(@width() / @charWidth)
|
||||
else
|
||||
Infinity
|
||||
currentSegmentLength = 0
|
||||
currentSegment = []
|
||||
segments = [currentSegment]
|
||||
|
||||
for token in @highlighter.tokensForRow(row)
|
||||
if (currentSegmentLength + token.value.length) <= maxSegmentLength
|
||||
currentSegmentLength += token.value.length
|
||||
currentSegment.push(token)
|
||||
else
|
||||
currentSegment = [token]
|
||||
currentSegmentLength = token.value.length
|
||||
segments.push(currentSegment)
|
||||
|
||||
segments = @lineWrapper.segmentsForRow(row)
|
||||
$$ ->
|
||||
for segment in segments
|
||||
@pre class: 'line', =>
|
||||
@ -148,6 +133,7 @@ class Editor extends View
|
||||
|
||||
setBuffer: (@buffer) ->
|
||||
@highlighter = new Highlighter(@buffer)
|
||||
@lineWrapper = new LineWrapper(Infinity, @highlighter)
|
||||
@undoManager = new UndoManager(@buffer)
|
||||
@renderLines()
|
||||
@setCursorPosition(row: 0, column: 0)
|
||||
@ -186,6 +172,13 @@ class Editor extends View
|
||||
@lines.find("pre.line:eq(#{row})")
|
||||
|
||||
setSoftWrap: (@softWrap) ->
|
||||
maxLength =
|
||||
if @softWrap
|
||||
Math.floor(@width() / @charWidth)
|
||||
else
|
||||
infinity
|
||||
|
||||
@lineWrapper.setMaxLength(maxLength)
|
||||
@renderLines()
|
||||
|
||||
clipPosition: ({row, column}) ->
|
||||
@ -199,7 +192,17 @@ class Editor extends View
|
||||
new Point(row, column)
|
||||
|
||||
pixelPositionFromPoint: ({row, column}) ->
|
||||
{ top: row * @lineHeight, left: column * @charWidth }
|
||||
segmentsAbove = 0
|
||||
segmentsAbove += @lineWrapper.segmentsForRow(i).length for i in [0...row]
|
||||
|
||||
for segment in @lineWrapper.segmentsForRow(row)
|
||||
if column > segment.lastIndex
|
||||
segmentsAbove++
|
||||
column -= segment.textLength
|
||||
else
|
||||
break
|
||||
|
||||
{ top: segmentsAbove * @lineHeight, left: column * @charWidth }
|
||||
|
||||
pointFromPixelPosition: ({top, left}) ->
|
||||
{ row: Math.floor(top / @lineHeight), column: Math.floor(left / @charWidth) }
|
||||
|
52
src/atom/line-wrapper.coffee
Normal file
52
src/atom/line-wrapper.coffee
Normal file
@ -0,0 +1,52 @@
|
||||
getWordRegex = -> /\b[^\s]+/g
|
||||
|
||||
module.exports =
|
||||
class LineWrapper
|
||||
constructor: (@maxLength, @highlighter) ->
|
||||
@buffer = @highlighter.buffer
|
||||
@segmentBuffer()
|
||||
|
||||
setMaxLength: (@maxLength) ->
|
||||
@segmentBuffer()
|
||||
|
||||
segmentBuffer: ->
|
||||
@lines = @segmentRows(0, @buffer.lastRow())
|
||||
|
||||
segmentsForRow: (row) ->
|
||||
@lines[row]
|
||||
|
||||
segmentRows: (start, end) ->
|
||||
for row in [start..end]
|
||||
@segmentRow(row)
|
||||
|
||||
segmentRow: (row) ->
|
||||
wordRegex = getWordRegex()
|
||||
line = @buffer.getLine(row)
|
||||
|
||||
breakIndices = []
|
||||
lastBreakIndex = 0
|
||||
|
||||
while match = wordRegex.exec(line)
|
||||
startIndex = match.index
|
||||
endIndex = startIndex + match[0].length
|
||||
if endIndex - lastBreakIndex > @maxLength
|
||||
breakIndices.push(startIndex)
|
||||
lastBreakIndex = startIndex
|
||||
|
||||
currentSegment = []
|
||||
currentSegment.lastIndex = 0
|
||||
segments = [currentSegment]
|
||||
nextBreak = breakIndices.shift()
|
||||
for token in @highlighter.tokensForRow(row)
|
||||
if currentSegment.lastIndex >= nextBreak
|
||||
nextBreak = breakIndices.shift()
|
||||
newSegment = []
|
||||
newSegment.lastIndex = currentSegment.lastIndex
|
||||
newSegment.textLength = 0
|
||||
segments.push(newSegment)
|
||||
currentSegment = newSegment
|
||||
currentSegment.push token
|
||||
currentSegment.lastIndex += token.value.length
|
||||
currentSegment.textLength += token.value.length
|
||||
|
||||
segments
|
Loading…
Reference in New Issue
Block a user