From 77f227c7e98c7b0ed9ad7b49808f7b09815185f6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 31 Jan 2012 21:59:58 -0700 Subject: [PATCH 01/11] WIP: Using ace tokenizer to break lines into tokens. Breaks some specs because the highlighter doesn't update when the buffer changes. Not loading the ace theme CSS yet, so you can't see any colors yet either. --- spec/atom/editor-spec.coffee | 14 ++++++++++++++ src/atom/buffer.coffee | 14 ++++++++++++++ src/atom/editor.coffee | 25 +++++++++++++++++-------- src/atom/highlighter.coffee | 25 +++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/atom/highlighter.coffee diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index adc408929..8ecb1ab47 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -25,6 +25,20 @@ describe "Editor", -> expect(buffer.getLine(10)).toBe '' expect(editor.lines.find('pre:eq(10)').html()).toBe ' ' + it "syntax highlights code based on the file type", -> + line1 = editor.lines.find('.line:first') + expect(line1.find('span:eq(0)')).toMatchSelector '.keyword.definition' + expect(line1.find('span:eq(0)').text()).toBe 'var' + expect(line1.find('span:eq(1)')).toMatchSelector '.text' + expect(line1.find('span:eq(1)').text()).toBe ' ' + expect(line1.find('span:eq(2)')).toMatchSelector '.identifier' + expect(line1.find('span:eq(2)').text()).toBe 'quicksort' + expect(line1.find('span:eq(4)')).toMatchSelector '.operator' + expect(line1.find('span:eq(4)').text()).toBe '=' + + line12 = editor.lines.find('.line:eq(11)') + expect(line12.find('span:eq(1)')).toMatchSelector '.keyword' + describe "cursor movement", -> describe ".setCursorPosition({row, column})", -> beforeEach -> diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index d06cf0994..c204ac61c 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -75,6 +75,9 @@ class Buffer numLines: -> @getLines().length + lastRow: -> + @numLines() - 1 + save: -> if not @path then throw new Error("Tried to save buffer with no url") fs.write @path, @getText() @@ -87,3 +90,14 @@ class Buffer trigger: (eventName, event) -> @eventHandlers?[eventName]?.forEach (handler) -> handler(event) + modeName: -> + extension = if @path then @path.split('/').pop().split('.').pop() else null + switch extension + when 'js' then 'javascript' + when 'coffee' then 'coffee' + when 'rb', 'ru' then 'ruby' + when 'c', 'h', 'cpp' then 'c_cpp' + when 'html', 'htm' then 'html' + when 'css' then 'css' + else 'text' + diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index a871ab29d..920855283 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -3,6 +3,8 @@ Buffer = require 'buffer' Point = require 'point' Cursor = require 'cursor' Selection = require 'selection' +Highlighter = require 'highlighter' + $ = require 'jquery' $$ = require 'template/builder' _ = require 'underscore' @@ -88,16 +90,23 @@ class Editor extends Template @hiddenInput.width(@charWidth) @focus() - buildLineElement: (lineText) -> - if lineText is '' - $$.pre class: "line", -> @raw(' ') - else - $$.pre class: "line", lineText + buildLineElement: (row) -> + tokens = @highlighter.tokensForLine(row) + $$.pre class: 'line', -> + if tokens.length + for token in tokens + classes = token.type.split('.').map((c) -> "ace_#{c}").join(' ') + @span { class: token.type.replace('.', ' ') }, token.value + else + @raw ' ' setBuffer: (@buffer) -> + @highlighter = new Highlighter(@buffer) + @lines.empty() - for line in @buffer.getLines() - @lines.append @buildLineElement(line) + for row in [0..@buffer.lastRow()] + line = @buildLineElement(row) + @lines.append line @setCursorPosition(row: 0, column: 0) @@ -130,7 +139,7 @@ class Editor extends Template element.text(line) insertLineElement: (row) -> - @getLineElement(row).before(@buildLineElement(@buffer.getLine(row))) + @getLineElement(row).before(@buildLineElement(row)) removeLineElement: (row) -> @getLineElement(row).remove() diff --git a/src/atom/highlighter.coffee b/src/atom/highlighter.coffee new file mode 100644 index 000000000..8aa977732 --- /dev/null +++ b/src/atom/highlighter.coffee @@ -0,0 +1,25 @@ +module.exports = +class Highlighter + buffer: null + tokenizer: null + lineTokens: [] + + constructor: (@buffer) -> + @buildTokenizer() + @tokenizeLines() + + buildTokenizer: -> + Mode = require("ace/mode/#{@buffer.modeName()}").Mode + @tokenizer = (new Mode).getTokenizer() + + tokenizeLines: -> + @lineTokens = [] + + state = "start" + for line in @buffer.getLines() + { state, tokens } = @tokenizer.getLineTokens(line, state) + @lineTokens.push tokens + + tokensForLine: (row) -> + @lineTokens[row] + From 23bcc68ac53cf5b70f94665c5213b6230e122dc5 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 1 Feb 2012 17:38:16 -0700 Subject: [PATCH 02/11] Highlighter updates when the buffer is changed --- spec/atom/highlighter-spec.coffee | 71 +++++++++++++++++++++++++++++++ src/atom/editor.coffee | 2 +- src/atom/highlighter.coffee | 25 ++++++----- 3 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 spec/atom/highlighter-spec.coffee diff --git a/spec/atom/highlighter-spec.coffee b/spec/atom/highlighter-spec.coffee new file mode 100644 index 000000000..71f8fbaf8 --- /dev/null +++ b/spec/atom/highlighter-spec.coffee @@ -0,0 +1,71 @@ +Highlighter = require 'highlighter' +Buffer = require 'buffer' +Range = require 'range' + +describe "Highlighter", -> + [highlighter, buffer] = [] + + beforeEach -> + buffer = new Buffer(require.resolve('fixtures/sample.js')) + highlighter = new Highlighter(buffer) + + describe "constructor", -> + it "tokenizes all the lines in the buffer", -> + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var') + expect(highlighter.tokensForRow(11)[1]).toEqual(type: 'keyword', value: 'return') + + describe "when the buffer changes", -> + describe "when a single line is changed", -> + it "updates tokens for the changed line", -> + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var') + buffer.change(new Range([0, 0], [0, 4]), '') + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'identifier', value: 'quicksort') + + it "preserves the scanning state when tokenizing the changed line" + # change the second line of a multi line comment and make sure it's still recognized as such + + describe "when multiple lines are updated, but none are added or removed", -> + it "updates tokens for each of the changed lines", -> + buffer.change(new Range([0, 0], [2, 0]), "foo()\nbar()\n") + + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'identifier', value: 'foo') + expect(highlighter.tokensForRow(1)[0]).toEqual(type: 'identifier', value: 'bar') + + # line 2 is unchanged + expect(highlighter.tokensForRow(2)[1]).toEqual(type: 'keyword', value: 'if') + + describe "when lines are both updated and removed", -> + it "updates tokens to reflect the removed lines", -> + buffer.change(new Range([1, 0], [3, 0]), "foo()") + + # previous line 0 remains + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var') + + # previous line 3 should be combined with input to form line 1 + expect(highlighter.tokensForRow(1)[0]).toEqual(type: 'identifier', value: 'foo') + expect(highlighter.tokensForRow(1)[6]).toEqual(type: 'identifier', value: 'pivot') + + # lines below deleted regions should be shifted upward + expect(highlighter.tokensForRow(2)[1]).toEqual(type: 'keyword', value: 'while') + expect(highlighter.tokensForRow(3)[1]).toEqual(type: 'identifier', value: 'current') + expect(highlighter.tokensForRow(4)[3]).toEqual(type: 'keyword.operator', value: '<') + + describe "when lines are both updated and inserted", -> + it "updates tokens to reflect the inserted lines", -> + buffer.change(new Range([1, 0], [2, 0]), "foo()\nbar()\nbaz()\nquux()") + + # previous line 0 remains + expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var') + + # 3 new lines inserted + expect(highlighter.tokensForRow(1)[0]).toEqual(type: 'identifier', value: 'foo') + expect(highlighter.tokensForRow(2)[0]).toEqual(type: 'identifier', value: 'bar') + expect(highlighter.tokensForRow(3)[0]).toEqual(type: 'identifier', value: 'baz') + + # previous line 2 is joined with quux() on line 4 + expect(highlighter.tokensForRow(4)[0]).toEqual(type: 'identifier', value: 'quux') + expect(highlighter.tokensForRow(4)[4]).toEqual(type: 'keyword', value: 'if') + + # previous line 3 is pushed down to become line 5 + expect(highlighter.tokensForRow(5)[3]).toEqual(type: 'identifier', value: 'pivot') + diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index 920855283..0c79688b3 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -91,7 +91,7 @@ class Editor extends Template @focus() buildLineElement: (row) -> - tokens = @highlighter.tokensForLine(row) + tokens = @highlighter.tokensForRow(row) $$.pre class: 'line', -> if tokens.length for token in tokens diff --git a/src/atom/highlighter.coffee b/src/atom/highlighter.coffee index 8aa977732..fe30518af 100644 --- a/src/atom/highlighter.coffee +++ b/src/atom/highlighter.coffee @@ -2,24 +2,29 @@ module.exports = class Highlighter buffer: null tokenizer: null - lineTokens: [] + tokensByRow: [] constructor: (@buffer) -> @buildTokenizer() - @tokenizeLines() + @tokensByRow = @tokenizeRows('start', 0, @buffer.lastRow()) + + @buffer.on 'change', (e) => + { preRange, postRange } = e + postRangeTokens = @tokenizeRows('start', postRange.start.row, postRange.end.row) + @tokensByRow[preRange.start.row..preRange.end.row] = postRangeTokens buildTokenizer: -> Mode = require("ace/mode/#{@buffer.modeName()}").Mode @tokenizer = (new Mode).getTokenizer() - tokenizeLines: -> - @lineTokens = [] + tokenizeRows: (state, start, end) -> + for row in [start..end] + { state, tokens } = @tokenizeRow(state, row) + tokens - state = "start" - for line in @buffer.getLines() - { state, tokens } = @tokenizer.getLineTokens(line, state) - @lineTokens.push tokens + tokenizeRow: (state, row) -> + @tokenizer.getLineTokens(@buffer.getLine(row), state) - tokensForLine: (row) -> - @lineTokens[row] + tokensForRow: (row) -> + @tokensByRow[row] From 1e6d4c618e1f7c44d89af8b9b7a2399c4bdb792a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 1 Feb 2012 20:19:26 -0700 Subject: [PATCH 03/11] Refactor Buffer.change --- src/atom/buffer.coffee | 60 +++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index 5bc3ebcdc..b78b5fa40 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -1,4 +1,6 @@ +_ = require 'underscore' fs = require 'fs' +Range = require 'range' module.exports = class Buffer @@ -35,42 +37,34 @@ class Buffer getLine: (row) -> @lines[row] - change: (preRange, string) -> - @remove(preRange) - postRange = @insert(preRange.start, string) - @trigger 'change', { preRange, postRange, string } + change: (preRange, newText) -> + postRange = new Range(_.clone(preRange.start), _.clone(preRange.start)) + prefix = @lines[preRange.start.row][0...preRange.start.column] + suffix = @lines[preRange.end.row][preRange.end.column..] + newTextLines = newText.split('\n') - remove: (range) -> - prefix = @lines[range.start.row][0...range.start.column] - suffix = @lines[range.end.row][range.end.column..] - @lines[range.start.row..range.end.row] = prefix + suffix - - insert: ({row, column}, string) -> - postRange = - start: { row, column } - end: { row, column } - - prefix = @lines[row][0...column] - suffix = @lines[row][column..] - - lines = string.split('\n') - - if lines.length == 1 - @lines[row] = prefix + string + suffix - postRange.end.column += string.length + if newTextLines.length == 1 + postRange.end.column += newText.length + linesToInsert = [prefix + newText + suffix] else - for line, i in lines - curRow = row + i - if i == 0 # replace first line - @lines[curRow] = prefix + line - else if i < lines.length - 1 # insert middle lines - @lines[curRow...curRow] = line - else # insert last line - @lines[curRow...curRow] = line + suffix - postRange.end.row = curRow - postRange.end.column = line.length + firstLineIndex = 0 + lastLineIndex = newTextLines.length - 1 - postRange + linesToInsert = + for line, i in newTextLines + switch i + when firstLineIndex + prefix + line + when lastLineIndex + postRange.end.row += i + postRange.end.column = line.length + line + suffix + else + line + + @lines[preRange.start.row..preRange.end.row] = linesToInsert + + @trigger 'change', { preRange, postRange, string: newText } numLines: -> @getLines().length From 6cde6952c75b3983eeda0ae0fe87c91cf417536a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 1 Feb 2012 20:32:59 -0700 Subject: [PATCH 04/11] :lipstick: --- src/atom/editor.coffee | 2 +- src/atom/selection.coffee | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index 9006f4555..53f2d3303 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -120,7 +120,7 @@ class Editor extends Template else @updateLineElement(row) - @selection.bufferChanged(e) + @cursor.bufferChanged(e) updateLineElement: (row) -> line = @buffer.getLine(row) diff --git a/src/atom/selection.coffee b/src/atom/selection.coffee index d34b85e05..51e94a61b 100644 --- a/src/atom/selection.coffee +++ b/src/atom/selection.coffee @@ -26,9 +26,6 @@ class Selection extends Template @anchor = null @updateAppearance() - bufferChanged: (e) -> - @cursor.setPosition(e.postRange.end) - updateAppearance: -> @clearRegions() From 104e75e0d7a57f12645ea7b7a401951888b96098 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 1 Feb 2012 21:52:07 -0700 Subject: [PATCH 05/11] WIP: Handle changes that affect highlighting of subsequent lines. Entering a /* at the top of the document will cause lines below to be interpreted as comments. Still needs cleanup. There are some unrelated failures associated with Buffer.setText not firing events correctly, which is causing the highlighter to get into an invalid state. --- spec/atom/highlighter-spec.coffee | 18 +++++++--------- src/atom/buffer.coffee | 4 +++- src/atom/highlighter.coffee | 35 ++++++++++++++++++++++--------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/spec/atom/highlighter-spec.coffee b/spec/atom/highlighter-spec.coffee index 71f8fbaf8..37cdb4d26 100644 --- a/spec/atom/highlighter-spec.coffee +++ b/spec/atom/highlighter-spec.coffee @@ -15,16 +15,7 @@ describe "Highlighter", -> expect(highlighter.tokensForRow(11)[1]).toEqual(type: 'keyword', value: 'return') describe "when the buffer changes", -> - describe "when a single line is changed", -> - it "updates tokens for the changed line", -> - expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'keyword.definition', value: 'var') - buffer.change(new Range([0, 0], [0, 4]), '') - expect(highlighter.tokensForRow(0)[0]).toEqual(type: 'identifier', value: 'quicksort') - - it "preserves the scanning state when tokenizing the changed line" - # change the second line of a multi line comment and make sure it's still recognized as such - - describe "when multiple lines are updated, but none are added or removed", -> + describe "when lines are updated, but none are added or removed", -> it "updates tokens for each of the changed lines", -> buffer.change(new Range([0, 0], [2, 0]), "foo()\nbar()\n") @@ -34,6 +25,13 @@ describe "Highlighter", -> # line 2 is unchanged expect(highlighter.tokensForRow(2)[1]).toEqual(type: 'keyword', value: 'if') + it "updates tokens for lines beyond the changed lines if needed", -> + buffer.insert([5, 30], '/* */') + buffer.insert([2, 0], '/*') + expect(highlighter.tokensForRow(3)[0].type).toBe 'comment' + expect(highlighter.tokensForRow(4)[0].type).toBe 'comment' + expect(highlighter.tokensForRow(5)[0].type).toBe 'comment' + describe "when lines are both updated and removed", -> it "updates tokens to reflect the removed lines", -> buffer.change(new Range([1, 0], [3, 0]), "foo()") diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index 0dd552337..a002a6e69 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -37,6 +37,9 @@ class Buffer getLine: (row) -> @lines[row] + insert: (point, text) -> + @change(new Range(point, point), text) + change: (preRange, newText) -> postRange = new Range(_.clone(preRange.start), _.clone(preRange.start)) prefix = @lines[preRange.start.row][0...preRange.start.column] @@ -63,7 +66,6 @@ class Buffer line @lines[preRange.start.row..preRange.end.row] = linesToInsert - @trigger 'change', { preRange, postRange, string: newText } numLines: -> diff --git a/src/atom/highlighter.coffee b/src/atom/highlighter.coffee index fe30518af..80604512d 100644 --- a/src/atom/highlighter.coffee +++ b/src/atom/highlighter.coffee @@ -1,30 +1,45 @@ +_ = require 'underscore' + module.exports = class Highlighter buffer: null tokenizer: null - tokensByRow: [] + lines: [] constructor: (@buffer) -> @buildTokenizer() - @tokensByRow = @tokenizeRows('start', 0, @buffer.lastRow()) - - @buffer.on 'change', (e) => - { preRange, postRange } = e - postRangeTokens = @tokenizeRows('start', postRange.start.row, postRange.end.row) - @tokensByRow[preRange.start.row..preRange.end.row] = postRangeTokens + @lines = @tokenizeRows('start', 0, @buffer.lastRow()) + @buffer.on 'change', (e) => @handleBufferChange(e) buildTokenizer: -> Mode = require("ace/mode/#{@buffer.modeName()}").Mode @tokenizer = (new Mode).getTokenizer() + handleBufferChange: (e) -> + { preRange, postRange } = e + + previousState = @lines[preRange.end.row].state + + newLines = @tokenizeRows('start', postRange.start.row, postRange.end.row) + @lines[preRange.start.row..preRange.end.row] = newLines + + row = postRange.end.row + 1 + state = _.last(newLines).state + until state == previousState + previousState = @lines[row].state + @lines[row] = line = @tokenizeRow(state, row) + { state } = line + row++ + tokenizeRows: (state, start, end) -> for row in [start..end] - { state, tokens } = @tokenizeRow(state, row) - tokens + line = @tokenizeRow(state, row) + state = line.state + line tokenizeRow: (state, row) -> @tokenizer.getLineTokens(@buffer.getLine(row), state) tokensForRow: (row) -> - @tokensByRow[row] + @lines[row].tokens From 5e95fc482dda6b9b0bceccd936e8bf8ddd97abaa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 2 Feb 2012 12:30:25 -0700 Subject: [PATCH 06/11] Buffer.setText emits the proper change events --- spec/atom/buffer-spec.coffee | 18 ++++++++++++++++++ src/atom/buffer.coffee | 18 +++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/spec/atom/buffer-spec.coffee b/spec/atom/buffer-spec.coffee index b58a37655..e22c3d43b 100644 --- a/spec/atom/buffer-spec.coffee +++ b/spec/atom/buffer-spec.coffee @@ -102,6 +102,24 @@ describe 'Buffer', -> expect(buffer.getLine(3)).toBe " var pivot = sort(Array.apply(this, arguments));" expect(buffer.getLine(4)).toBe "};" + describe ".setText(text)", -> + it "changes the entire contents of the buffer and emits a change event", -> + lastRow = buffer.lastRow() + expectedPreRange = new Range([0,0], [lastRow, buffer.getLine(lastRow).length]) + changeHandler = jasmine.createSpy('changeHandler') + buffer.on 'change', changeHandler + + newText = "I know you are.\nBut what am I?" + buffer.setText(newText) + + expect(buffer.getText()).toBe newText + expect(changeHandler).toHaveBeenCalled() + + [event] = changeHandler.argsForCall[0] + expect(event.string).toBe newText + expect(event.preRange).toEqual expectedPreRange + expect(event.postRange).toEqual(new Range([0, 0], [1, 14])) + describe ".save()", -> describe "when the buffer has a path", -> filePath = null diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index b78b5fa40..7fe469b85 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -8,6 +8,7 @@ class Buffer constructor: (@path) -> @url = @path # we want this to be path on master, but let's not break it on a branch + @lines = [''] if @path and fs.exists(@path) @setText(fs.read(@path)) else @@ -17,7 +18,10 @@ class Buffer @lines.join('\n') setText: (text) -> - @lines = text.split('\n') + @change(@getRange(), text) + + getRange: -> + new Range([0, 0], [@lastRow(), @lastLine().length]) getTextInRange: (range) -> if range.start.row == range.end.row @@ -37,6 +41,15 @@ class Buffer getLine: (row) -> @lines[row] + numLines: -> + @getLines().length + + lastRow: -> + @getLines().length - 1 + + lastLine: -> + @getLine(@lastRow()) + change: (preRange, newText) -> postRange = new Range(_.clone(preRange.start), _.clone(preRange.start)) prefix = @lines[preRange.start.row][0...preRange.start.column] @@ -66,8 +79,7 @@ class Buffer @trigger 'change', { preRange, postRange, string: newText } - numLines: -> - @getLines().length + save: -> if not @path then throw new Error("Tried to save buffer with no url") From 00bc17baf0a74420faea9f5fe308331a23118eb1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 2 Feb 2012 12:47:35 -0700 Subject: [PATCH 07/11] WIP: Add theme/twilight.css. Lines aren't being updated properly quite yet, and the stylesheet needs more tweaking, but colors are there when you load a file. --- src/atom/editor.coffee | 1 + static/theme/twilight.css | 134 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 static/theme/twilight.css diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index 4068426d7..e2428eda8 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -28,6 +28,7 @@ class Editor extends Template initialize: () -> requireStylesheet 'editor.css' + requireStylesheet 'theme/twilight.css' @bindKeys() @buildCursorAndSelection() @handleEvents() diff --git a/static/theme/twilight.css b/static/theme/twilight.css new file mode 100644 index 000000000..1f782f6b9 --- /dev/null +++ b/static/theme/twilight.css @@ -0,0 +1,134 @@ +.editor { + border: 2px solid rgb(159, 159, 159); +} + +.editor.focus { + border: 2px solid #327fbd; +} + +.gutter { + background: #e8e8e8; + color: #333; +} + +.print_margin { + width: 1px; + background: #e8e8e8; +} + +.scroller { + background-color: #141414; +} + +.text-layer { + cursor: text; + color: #F8F8F8; +} + +.cursor { + border-left: 2px solid #A7A7A7; +} + +.cursor.overwrite { + border-left: 0px; + border-bottom: 1px solid #A7A7A7; +} + +.marker-layer .selection { + background: rgba(221, 240, 255, 0.20); +} + +.marker-layer .step { + background: rgb(198, 219, 174); +} + +.marker-layer .bracket { + margin: -1px 0 0 -1px; + border: 1px solid rgba(255, 255, 255, 0.25); +} + +.marker-layer .active_line { + background: rgba(255, 255, 255, 0.031); +} + +.marker-layer .selected_word { + border: 1px solid rgba(221, 240, 255, 0.20); +} + +.invisible { + color: rgba(255, 255, 255, 0.25); +} + +.keyword { + color:#CDA869; +} + +.constant { + color:#CF6A4C; +} + +.invalid.illegal { + color:#F8F8F8; +background-color:rgba(86, 45, 86, 0.75); +} + +.invalid.deprecated { + text-decoration:underline; +font-style:italic; +color:#D2A8A1; +} + +.support { + color:#9B859D; +} + +.fold { + background-color: #AC885B; + border-color: #F8F8F8; +} + +.support.function { + color:#DAD085; +} + +.string { + color:#8F9D6A; +} + +.string.regexp { + color:#E9C062; +} + +.comment { + font-style:italic; +color:#5F5A60; +} + +.variable { + color:#7587A6; +} + +.xml_pe { + color:#494949; +} + +.meta.tag { + color:#AC885B; +} + +.entity.name.function { + color:#AC885B; +} + +.markup.underline { + text-decoration:underline; +} + +.markup.heading { + color:#CF6A4C; +} + +.markup.list { + color:#F9EE98; +}"; + From 470ec2e5e442d87d908ab4204e3a8b3f97909425 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 2 Feb 2012 16:39:42 -0700 Subject: [PATCH 08/11] Refactor Highligher.handleBufferChange Clarify the logic of extending the re-highlighting beyond the scope of the textual change. --- src/atom/highlighter.coffee | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/atom/highlighter.coffee b/src/atom/highlighter.coffee index 80604512d..33c4a5a74 100644 --- a/src/atom/highlighter.coffee +++ b/src/atom/highlighter.coffee @@ -19,20 +19,18 @@ class Highlighter { preRange, postRange } = e previousState = @lines[preRange.end.row].state - newLines = @tokenizeRows('start', postRange.start.row, postRange.end.row) @lines[preRange.start.row..preRange.end.row] = newLines - row = postRange.end.row + 1 - state = _.last(newLines).state - until state == previousState - previousState = @lines[row].state - @lines[row] = line = @tokenizeRow(state, row) - { state } = line - row++ + for row in [postRange.end.row...@buffer.lastRow()] + break if @lines[row].state == previousState + nextRow = row + 1 + previousState = @lines[nextRow].state + @lines[nextRow] = @tokenizeRow(@lines[row].state, nextRow) - tokenizeRows: (state, start, end) -> - for row in [start..end] + tokenizeRows: (startState, startRow, endRow) -> + state = startState + for row in [startRow..endRow] line = @tokenizeRow(state, row) state = line.state line From 6ed33fbd1730774c828efc1aa04de507e4ea867a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 2 Feb 2012 16:39:56 -0700 Subject: [PATCH 09/11] :lipstick: --- src/atom/buffer.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index 4839e22d2..6bf77c0d3 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -41,9 +41,6 @@ class Buffer getLine: (row) -> @lines[row] - insert: (point, text) -> - @change(new Range(point, point), text) - numLines: -> @getLines().length @@ -53,6 +50,9 @@ class Buffer lastLine: -> @getLine(@lastRow()) + insert: (point, text) -> + @change(new Range(point, point), text) + change: (preRange, newText) -> postRange = new Range(_.clone(preRange.start), _.clone(preRange.start)) prefix = @lines[preRange.start.row][0...preRange.start.column] From 71e5462611c748185236c3beb862db88f01fc764 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 2 Feb 2012 16:49:12 -0700 Subject: [PATCH 10/11] Refactor Buffer.change --- src/atom/buffer.coffee | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/atom/buffer.coffee b/src/atom/buffer.coffee index 6bf77c0d3..661f82cce 100644 --- a/src/atom/buffer.coffee +++ b/src/atom/buffer.coffee @@ -57,28 +57,20 @@ class Buffer postRange = new Range(_.clone(preRange.start), _.clone(preRange.start)) prefix = @lines[preRange.start.row][0...preRange.start.column] suffix = @lines[preRange.end.row][preRange.end.column..] + newTextLines = newText.split('\n') if newTextLines.length == 1 postRange.end.column += newText.length - linesToInsert = [prefix + newText + suffix] + newTextLines = [prefix + newText + suffix] else - firstLineIndex = 0 lastLineIndex = newTextLines.length - 1 + newTextLines[0] = prefix + newTextLines[0] + postRange.end.row += lastLineIndex + postRange.end.column = newTextLines[lastLineIndex].length + newTextLines[lastLineIndex] += suffix - linesToInsert = - for line, i in newTextLines - switch i - when firstLineIndex - prefix + line - when lastLineIndex - postRange.end.row += i - postRange.end.column = line.length - line + suffix - else - line - - @lines[preRange.start.row..preRange.end.row] = linesToInsert + @lines[preRange.start.row..preRange.end.row] = newTextLines @trigger 'change', { preRange, postRange, string: newText } save: -> From 0120df540afce7c083d3ca27cd6460117da12fe2 Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 2 Feb 2012 17:04:37 -0700 Subject: [PATCH 11/11] Lines remain syntax-highlighted when they are updated. --- spec/atom/editor-spec.coffee | 6 ++++++ src/atom/editor.coffee | 7 +------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index 8ecb1ab47..c7707e076 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -39,6 +39,12 @@ describe "Editor", -> line12 = editor.lines.find('.line:eq(11)') expect(line12.find('span:eq(1)')).toMatchSelector '.keyword' + describe "when lines are updated in the buffer", -> + it "syntax highlights the updated lines", -> + expect(editor.lines.find('.line:eq(0) span:eq(0)')).toMatchSelector '.keyword.definition' + buffer.insert([0, 4], "g") + expect(editor.lines.find('.line:eq(0) span:eq(0)')).toMatchSelector '.keyword.definition' + describe "cursor movement", -> describe ".setCursorPosition({row, column})", -> beforeEach -> diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index e2428eda8..86eded41a 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -133,12 +133,7 @@ class Editor extends Template @cursor.bufferChanged(e) updateLineElement: (row) -> - line = @buffer.getLine(row) - element = @getLineElement(row) - if line == '' - element.html(' ') - else - element.text(line) + @getLineElement(row).replaceWith(@buildLineElement(row)) insertLineElement: (row) -> @getLineElement(row).before(@buildLineElement(row))