Skip to content

Commit 8ec60c8

Browse files
committed
Copy misc/blocks to lib_src
1 parent 7fc7bfc commit 8ec60c8

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

lib_src/misc/blocks.js

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
'use babel'
2+
// TODO: docstrings
3+
4+
import { forLines } from './scopes'
5+
6+
export function getLine (ed, l) {
7+
return {
8+
scope: ed.scopeDescriptorForBufferPosition([l, 0]).scopes,
9+
line: ed.getTextInBufferRange([[l, 0], [l, Infinity]])
10+
}
11+
}
12+
13+
function isBlank ({line, scope}, allowDocstrings = false) {
14+
for (const s of scope) {
15+
if (/\bcomment\b/.test(s) || (!allowDocstrings && /\bdocstring\b/.test(s))) {
16+
return true
17+
}
18+
}
19+
return /^\s*(#.*)?$/.test(line)
20+
}
21+
function isEnd ({ line, scope }) {
22+
if (isStringEnd({ line, scope })) {
23+
return true
24+
}
25+
return /^(end\b|\)|\]|\})/.test(line)
26+
}
27+
function isStringEnd ({ line, scope }) {
28+
scope = scope.join(' ')
29+
return /\bstring\.multiline\.end\b/.test(scope) ||
30+
(/\bstring\.end\b/.test(scope) && /\bbacktick\b/.test(scope))
31+
}
32+
function isCont ({ line, scope }) {
33+
scope = scope.join(' ')
34+
if (/\bstring\b/.test(scope) && !(/\bpunctuation\.definition\.string\b/.test(scope))) {
35+
return true
36+
}
37+
38+
return line.match(/^(else|elseif|catch|finally)\b/)
39+
}
40+
function isStart (lineInfo) {
41+
return !(/^\s/.test(lineInfo.line) || isBlank(lineInfo) || isEnd(lineInfo) || isCont(lineInfo))
42+
}
43+
44+
function walkBack(ed, row) {
45+
while ((row > 0) && !isStart(getLine(ed, row))) {
46+
row--
47+
}
48+
return row
49+
}
50+
51+
function walkForward (ed, start) {
52+
let end = start
53+
let mark = start
54+
while (mark < ed.getLastBufferRow()) {
55+
mark++
56+
const lineInfo = getLine(ed, mark)
57+
58+
if (isStart(lineInfo)) {
59+
break
60+
}
61+
if (isEnd(lineInfo)) {
62+
// An `end` only counts when there still are unclosed blocks (indicated by `forLines`
63+
// returning a non-empty array).
64+
// If the line closes a multiline string we also take that as ending the block.
65+
if (
66+
!(forLines(ed, start, mark-1).length === 0) ||
67+
isStringEnd(lineInfo)
68+
) {
69+
end = mark
70+
}
71+
} else if (!(isBlank(lineInfo) || isStart(lineInfo))) {
72+
end = mark
73+
}
74+
}
75+
return end
76+
}
77+
78+
function getRange (ed, row) {
79+
const start = walkBack(ed, row)
80+
const end = walkForward(ed, start)
81+
if (start <= row && row <= end) {
82+
return [[start, 0], [end, Infinity]]
83+
}
84+
}
85+
86+
function getSelection (ed, sel) {
87+
const {start, end} = sel.getBufferRange()
88+
const range = [[start.row, start.column], [end.row, end.column]]
89+
while (isBlank(getLine(ed, range[0][0]), true) && (range[0][0] <= range[1][0])) {
90+
range[0][0]++
91+
range[0][1] = 0
92+
}
93+
while (isBlank(getLine(ed, range[1][0]), true) && (range[1][0] >= range[0][0])) {
94+
range[1][0]--
95+
range[1][1] = Infinity
96+
}
97+
return range
98+
}
99+
100+
export function moveNext (ed, sel, range) {
101+
// Ensure enough room at the end of the buffer
102+
const row = range[1][0]
103+
let last
104+
while ((last = ed.getLastBufferRow()) < (row+2)) {
105+
if ((last !== row) && !isBlank(getLine(ed, last))) {
106+
break
107+
}
108+
sel.setBufferRange([[last, Infinity], [last, Infinity]])
109+
sel.insertText('\n')
110+
}
111+
// Move the cursor
112+
let to = row + 1
113+
while ((to < ed.getLastBufferRow()) && isBlank(getLine(ed, to))) {
114+
to++
115+
}
116+
to = walkForward(ed, to)
117+
return sel.setBufferRange([[to, Infinity], [to, Infinity]])
118+
}
119+
120+
function getRanges (ed) {
121+
const ranges = ed.getSelections().map(sel => {
122+
return {
123+
selection: sel,
124+
range: sel.isEmpty() ?
125+
getRange(ed, sel.getHeadBufferPosition().row) :
126+
getSelection(ed, sel)
127+
}
128+
})
129+
return ranges.filter(({ range }) => {
130+
return range && ed.getTextInBufferRange(range).trim()
131+
})
132+
}
133+
134+
export function get (ed) {
135+
return getRanges(ed).map(({ range, selection }) => {
136+
return {
137+
range,
138+
selection,
139+
line: range[0][0],
140+
text: ed.getTextInBufferRange(range)
141+
}
142+
})
143+
}
144+
145+
export function getLocalContext (editor, row) {
146+
const range = getRange(editor, row)
147+
const context = range ? editor.getTextInBufferRange(range) : ''
148+
// NOTE:
149+
// backend code expects startRow to be number for most cases, e.g.: `row = row - startRow`
150+
// so let's just return `0` when there is no local context
151+
// to check there is a context or not, just check `isempty(context)`
152+
const startRow = range ? range[0][0] : 0
153+
return {
154+
context,
155+
startRow
156+
}
157+
}
158+
159+
export function select (ed = atom.workspace.getActiveTextEditor()) {
160+
if (!ed) return
161+
return ed.mutateSelectedText(selection => {
162+
const range = getRange(ed, selection.getHeadBufferPosition().row)
163+
if (range) {
164+
selection.setBufferRange(range)
165+
}
166+
})
167+
}

0 commit comments

Comments
 (0)