Skip to content

Instantly share code, notes, and snippets.

@shspage
Last active February 6, 2017 15:07
Show Gist options
  • Save shspage/bdc4dac69e4aacc6c7cf99e3e3b29e78 to your computer and use it in GitHub Desktop.
Save shspage/bdc4dac69e4aacc6c7cf99e3e3b29e78 to your computer and use it in GitHub Desktop.
atomのSublime-Style-Column-Selectionを力技っぽく全角対応にしてみた。 もう少し工夫が要りそう
# atomのSublime-Style-Column-Selection(*)を力技っぽく全角対応にしてみた。
# もう少し工夫が要りそう
# coffeescript自信ない
# * v.1.7.2 @bigfive https://github.com/bigfive/atom-sublime-select
# (displayBufferのエラー対策に@Greenekさんのpull request #127を適用)
# TODO:
# 桁数の取得部分はscreenPositionForPixelPosition/PixelPositionForScreenPositionを使って書き直す
{Point} = require 'atom'
module.exports =
class SublimeSelectEditorHandler
constructor: (editor, inputCfg) ->
@editor = editor
@inputCfg = inputCfg
@_resetState()
@_setup_vars()
@rexHankakuKana = /[\uFF61-\uFF9F]/g
@rexSurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
subscribe: ->
@selection_observer = @editor.onDidChangeSelectionRange @onRangeChange
@editorElement.addEventListener 'mousedown', @onMouseDown
@editorElement.addEventListener 'mousemove', @onMouseMove
@editorElement.addEventListener 'mouseup', @onMouseEventToHijack
@editorElement.addEventListener 'mouseleave', @onMouseEventToHijack
@editorElement.addEventListener 'mouseenter', @onMouseEventToHijack
@editorElement.addEventListener 'contextmenu', @onMouseEventToHijack
@editorElement.addEventListener 'blur', @onBlur
unsubscribe: ->
@_resetState()
@selection_observer.dispose()
@editorElement.removeEventListener 'mousedown', @onMouseDown
@editorElement.removeEventListener 'mousemove', @onMouseMove
@editorElement.removeEventListener 'mouseup', @onMouseEventToHijack
@editorElement.removeEventListener 'mouseleave', @onMouseEventToHijack
@editorElement.removeEventListener 'mouseenter', @onMouseEventToHijack
@editorElement.removeEventListener 'contextmenu', @onMouseEventToHijack
@editorElement.removeEventListener 'blur', @onBlur
# -------
# Event Handlers
# -------
onMouseDown: (e) =>
if @mouseStartPos
e.preventDefault()
return false
if @_mainMouseAndKeyDown(e)
@_resetState()
@mouseStartPos = @_screenPositionForMouseEvent(e)
@mouseStartPos.column = @_fixColumnCount(@mouseStartPos)
@mouseEndPos = @mouseStartPos
e.preventDefault()
return false
onMouseMove: (e) =>
if @mouseStartPos
e.preventDefault()
if @_mainMouseDown(e)
@mouseEndPos = @_screenPositionForMouseEvent(e)
return if @mouseEndPos.isEqual @mouseEndPosPrev
@_selectBoxAroundCursors()
@mouseEndPosPrev = @mouseEndPos
return false
if e.which == 0
@_resetState()
# Hijack all the mouse events while selecting
onMouseEventToHijack: (e) =>
if @mouseStartPos
e.preventDefault()
return false
onBlur: (e) =>
@_resetState()
onRangeChange: (newVal) =>
if @mouseStartPos and !newVal.selection.isSingleScreenLine()
newVal.selection.destroy()
@_selectBoxAroundCursors()
# -------
# Methods
# -------
_resetState: ->
@mouseStartPos = null
@mouseEndPos = null
_setup_vars: ->
@editorElement ?= atom.views.getView @editor
@editorComponent ?= @editorElement.component
# I had to create my own version of @editorComponent.screenPositionFromMouseEvent
# The @editorBuffer one doesnt quite do what I need
_screenPositionForMouseEvent: (e) ->
@_setup_vars()
pixelPosition = @editorComponent.pixelPositionForMouseEvent(e)
targetTop = pixelPosition.top
targetLeft = pixelPosition.left
defaultCharWidth = @editor.getDefaultCharWidth()
row = Math.floor(targetTop / @editor.getLineHeightInPixels())
targetLeft = Infinity if row > @editor.getLastBufferRow()
row = Math.min(row, @editor.getLastBufferRow())
row = Math.max(0, row)
column = Math.round (targetLeft) / defaultCharWidth
new Point(row, column)
# methods for checking mouse/key state against config
_mainMouseDown: (e) ->
e.which is @inputCfg.mouseNum
_mainMouseAndKeyDown: (e) ->
if @inputCfg.selectKey
@_mainMouseDown(e) and e[@inputCfg.selectKey]
else
@_mainMouseDown(e)
_charByteLen: (c) ->
if escape(c).length < 4 then 1 else 2
_fixSpecialChar: (s) ->
s = s.replace(@rexHankakuKana, ".")
s.replace(@rexSurrogate, "..")
_byteLen: (s) ->
n = 0
if s != ""
s = @_fixSpecialChar(s)
for i in [0..s.length-1]
n += @_charByteLen(s.charAt(i))
n
_getStrBeforeMouse: (pos, s) ->
s = @_fixSpecialChar(s)
n = 0
for i in [0..s.length-1]
n += @_charByteLen(s.charAt(i))
if n > pos
break
s.substr(0, i)
_fixColumnCount: (point) ->
rowText = @editor.lineTextForScreenRow(point.row)
@_byteLen(@_getStrBeforeMouse(point.column, rowText))
# Do the actual selecting
_selectBoxAroundCursors: ->
if @mouseStartPos and @mouseEndPos
ranges = []
for row in [@mouseStartPos.row..@mouseEndPos.row]
@mouseEndPos.column = 0 if @mouseEndPos.column < 0
rowText = @editor.lineTextForScreenRow(row)
rowLength = @_byteLen(rowText)
strBeforeStart = @_getStrBeforeMouse(@mouseStartPos.column, rowText)
strBeforeEnd = @_getStrBeforeMouse(@mouseEndPos.column, rowText)
if rowLength > @mouseStartPos.column or rowLength > @_byteLen(strBeforeEnd)
range = [[row, strBeforeStart.length], [row, strBeforeEnd.length]]
ranges.push range
if ranges.length
isReversed = @mouseEndPos.column < @mouseStartPos.column
@editor.setSelectedScreenRanges ranges, {reversed: isReversed}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment