File manager - Edit - /home/newsbmcs.com/public_html/static/img/logo/line.tar
Back
pos.js 0000644 00000002770 15030043631 0005704 0 ustar 00 import { getLine } from "./utils_line.js" // A Pos instance represents a position within the text. export function Pos(line, ch, sticky = null) { if (!(this instanceof Pos)) return new Pos(line, ch, sticky) this.line = line this.ch = ch this.sticky = sticky } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } export function copyPos(x) {return Pos(x.line, x.ch)} export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} export function clipPos(doc, pos) { if (pos.line < doc.first) return Pos(doc.first, 0) let last = doc.first + doc.size - 1 if (pos.line > last) return Pos(last, getLine(doc, last).text.length) return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { let ch = pos.ch if (ch == null || ch > linelen) return Pos(pos.line, linelen) else if (ch < 0) return Pos(pos.line, 0) else return pos } export function clipPosArray(doc, array) { let out = [] for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) return out } highlight.js 0000644 00000023627 15030043631 0007056 0 ustar 00 import { countColumn } from "../util/misc.js" import { copyState, innerMode, startState } from "../modes.js" import StringStream from "../util/StringStream.js" import { getLine, lineNo } from "./utils_line.js" import { clipPos } from "./pos.js" class SavedContext { constructor(state, lookAhead) { this.state = state this.lookAhead = lookAhead } } class Context { constructor(doc, state, line, lookAhead) { this.state = state this.doc = doc this.line = line this.maxLookAhead = lookAhead || 0 this.baseTokens = null this.baseTokenPos = 1 } lookAhead(n) { let line = this.doc.getLine(this.line + n) if (line != null && n > this.maxLookAhead) this.maxLookAhead = n return line } baseToken(n) { if (!this.baseTokens) return null while (this.baseTokens[this.baseTokenPos] <= n) this.baseTokenPos += 2 let type = this.baseTokens[this.baseTokenPos + 1] return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} } nextLine() { this.line++ if (this.maxLookAhead > 0) this.maxLookAhead-- } static fromSaved(doc, saved, line) { if (saved instanceof SavedContext) return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) else return new Context(doc, copyState(doc.mode, saved), line) } save(copy) { let state = copy !== false ? copyState(this.doc.mode, this.state) : this.state return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state } } // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. export function highlightLine(cm, line, context, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). let st = [cm.state.modeGen], lineClasses = {} // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, context, (end, style) => st.push(end, style), lineClasses, forceToEnd) let state = context.state // Run overlays, adjust style array. for (let o = 0; o < cm.state.overlays.length; ++o) { context.baseTokens = st let overlay = cm.state.overlays[o], i = 1, at = 0 context.state = true runMode(cm, line.text, overlay.mode, context, (end, style) => { let start = i // Ensure there's a token end at the current position, and that i points at it while (at < end) { let i_end = st[i] if (i_end > end) st.splice(i, 1, end, st[i+1], i_end) i += 2 at = Math.min(end, i_end) } if (!style) return if (overlay.opaque) { st.splice(start, i - start, end, "overlay " + style) i = start + 2 } else { for (; start < i; start += 2) { let cur = st[start+1] st[start+1] = (cur ? cur + " " : "") + "overlay " + style } } }, lineClasses) context.state = state context.baseTokens = null context.baseTokenPos = 1 } return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } export function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { let context = getContextBefore(cm, lineNo(line)) let resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state) let result = highlightLine(cm, line, context) if (resetState) context.state = resetState line.stateAfter = context.save(!resetState) line.styles = result.styles if (result.classes) line.styleClasses = result.classes else if (line.styleClasses) line.styleClasses = null if (updateFrontier === cm.doc.highlightFrontier) cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier) } return line.styles } export function getContextBefore(cm, n, precise) { let doc = cm.doc, display = cm.display if (!doc.mode.startState) return new Context(doc, true, n) let start = findStartLine(cm, n, precise) let saved = start > doc.first && getLine(doc, start - 1).stateAfter let context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start) doc.iter(start, n, line => { processLine(cm, line.text, context) let pos = context.line line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null context.nextLine() }) if (precise) doc.modeFrontier = context.line return context } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. export function processLine(cm, text, context, startAt) { let mode = cm.doc.mode let stream = new StringStream(text, cm.options.tabSize, context) stream.start = stream.pos = startAt || 0 if (text == "") callBlankLine(mode, context.state) while (!stream.eol()) { readToken(mode, stream, context.state) stream.start = stream.pos } } function callBlankLine(mode, state) { if (mode.blankLine) return mode.blankLine(state) if (!mode.innerMode) return let inner = innerMode(mode, state) if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) } function readToken(mode, stream, state, inner) { for (let i = 0; i < 10; i++) { if (inner) inner[0] = innerMode(mode, state).mode let style = mode.token(stream, state) if (stream.pos > stream.start) return style } throw new Error("Mode " + mode.name + " failed to advance stream.") } class Token { constructor(stream, type, state) { this.start = stream.start; this.end = stream.pos this.string = stream.current() this.type = type || null this.state = state } } // Utility for getTokenAt and getLineTokens export function takeToken(cm, pos, precise, asArray) { let doc = cm.doc, mode = doc.mode, style pos = clipPos(doc, pos) let line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise) let stream = new StringStream(line.text, cm.options.tabSize, context), tokens if (asArray) tokens = [] while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos style = readToken(mode, stream, context.state) if (asArray) tokens.push(new Token(stream, style, copyState(doc.mode, context.state))) } return asArray ? tokens : new Token(stream, style, context.state) } function extractLineClasses(type, output) { if (type) for (;;) { let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) if (!lineClass) break type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) let prop = lineClass[1] ? "bgClass" : "textClass" if (output[prop] == null) output[prop] = lineClass[2] else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) output[prop] += " " + lineClass[2] } return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { let flattenSpans = mode.flattenSpans if (flattenSpans == null) flattenSpans = cm.options.flattenSpans let curStart = 0, curStyle = null let stream = new StringStream(text, cm.options.tabSize, context), style let inner = cm.options.addModeClass && [null] if (text == "") extractLineClasses(callBlankLine(mode, context.state), lineClasses) while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false if (forceToEnd) processLine(cm, text, context, stream.pos) stream.pos = text.length style = null } else { style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses) } if (inner) { let mName = inner[0].name if (mName) style = "m-" + (style ? mName + " " + style : mName) } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 5000) f(curStart, curStyle) } curStyle = style } stream.start = stream.pos } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. let pos = Math.min(stream.pos, curStart + 5000) f(pos, curStyle) curStart = pos } } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { let minindent, minline, doc = cm.doc let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) for (let search = n; search > lim; --search) { if (search <= doc.first) return doc.first let line = getLine(doc, search - 1), after = line.stateAfter if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) return search let indented = countColumn(line.text, null, cm.options.tabSize) if (minline == null || minindent > indented) { minline = search - 1 minindent = indented } } return minline } export function retreatFrontier(doc, n) { doc.modeFrontier = Math.min(doc.modeFrontier, n) if (doc.highlightFrontier < n - 10) return let start = doc.first for (let line = n - 1; line > start; line--) { let saved = getLine(doc, line).stateAfter // change is on 3 // state on line 1 looked ahead 2 -- so saw 3 // test 1 + 2 < 3 should cover this if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { start = line + 1 break } } doc.highlightFrontier = Math.min(doc.highlightFrontier, start) } utils_line.js 0000644 00000005015 15030043631 0007245 0 ustar 00 import { indexOf } from "../util/misc.js" // Find the line object corresponding to the given line number. export function getLine(doc, n) { n -= doc.first if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") let chunk = doc while (!chunk.lines) { for (let i = 0;; ++i) { let child = chunk.children[i], sz = child.chunkSize() if (n < sz) { chunk = child; break } n -= sz } } return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. export function getBetween(doc, start, end) { let out = [], n = start.line doc.iter(start.line, end.line + 1, line => { let text = line.text if (n == end.line) text = text.slice(0, end.ch) if (n == start.line) text = text.slice(start.ch) out.push(text) ++n }) return out } // Get the lines between from and to, as array of strings. export function getLines(doc, from, to) { let out = [] doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value return out } // Update the height of a line, propagating the height change // upwards to parent nodes. export function updateLineHeight(line, height) { let diff = height - line.height if (diff) for (let n = line; n; n = n.parent) n.height += diff } // Given a line object, find its line number by walking up through // its parent links. export function lineNo(line) { if (line.parent == null) return null let cur = line.parent, no = indexOf(cur.lines, line) for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (let i = 0;; ++i) { if (chunk.children[i] == cur) break no += chunk.children[i].chunkSize() } } return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. export function lineAtHeight(chunk, h) { let n = chunk.first outer: do { for (let i = 0; i < chunk.children.length; ++i) { let child = chunk.children[i], ch = child.height if (h < ch) { chunk = child; continue outer } h -= ch n += child.chunkSize() } return n } while (!chunk.lines) let i = 0 for (; i < chunk.lines.length; ++i) { let line = chunk.lines[i], lh = line.height if (h < lh) break h -= lh } return n + i } export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} export function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)) } saw_special_spans.js 0000644 00000000375 15030043631 0010600 0 ustar 00 // Optimize some code when these features are not used. export let sawReadOnlySpans = false, sawCollapsedSpans = false export function seeReadOnlySpans() { sawReadOnlySpans = true } export function seeCollapsedSpans() { sawCollapsedSpans = true } spans.js 0000644 00000033425 15030043631 0006230 0 ustar 00 import { indexOf, lst } from "../util/misc.js" import { cmp } from "./pos.js" import { sawCollapsedSpans } from "./saw_special_spans.js" import { getLine, isLine, lineNo } from "./utils_line.js" // TEXTMARKER SPANS export function MarkedSpan(marker, from, to) { this.marker = marker this.from = from; this.to = to } // Search an array of spans for a span matching the given marker. export function getMarkedSpanFor(spans, marker) { if (spans) for (let i = 0; i < spans.length; ++i) { let span = spans[i] if (span.marker == marker) return span } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). export function removeMarkedSpan(spans, span) { let r for (let i = 0; i < spans.length; ++i) if (spans[i] != span) (r || (r = [])).push(spans[i]) return r } // Add a span to a line. export function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span] span.marker.attachLine(line) } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { let nw if (old) for (let i = 0; i < old.length; ++i) { let span = old[i], marker = span.marker let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) } } return nw } function markedSpansAfter(old, endCh, isInsert) { let nw if (old) for (let i = 0; i < old.length; ++i) { let span = old[i], marker = span.marker let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)) } } return nw } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. export function stretchSpansOverChange(doc, change) { if (change.full) return null let oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans let oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans if (!oldFirst && !oldLast) return null let startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 // Get the spans that 'stick out' on both sides let first = markedSpansBefore(oldFirst, startCh, isInsert) let last = markedSpansAfter(oldLast, endCh, isInsert) // Next, merge those two ends let sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0) if (first) { // Fix up .to properties of first for (let i = 0; i < first.length; ++i) { let span = first[i] if (span.to == null) { let found = getMarkedSpanFor(last, span.marker) if (!found) span.to = startCh else if (sameLine) span.to = found.to == null ? null : found.to + offset } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (let i = 0; i < last.length; ++i) { let span = last[i] if (span.to != null) span.to += offset if (span.from == null) { let found = getMarkedSpanFor(first, span.marker) if (!found) { span.from = offset if (sameLine) (first || (first = [])).push(span) } } else { span.from += offset if (sameLine) (first || (first = [])).push(span) } } } // Make sure we didn't create any zero-length spans if (first) first = clearEmptySpans(first) if (last && last != first) last = clearEmptySpans(last) let newMarkers = [first] if (!sameLine) { // Fill gap with whole-line-spans let gap = change.text.length - 2, gapMarkers if (gap > 0 && first) for (let i = 0; i < first.length; ++i) if (first[i].to == null) (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)) for (let i = 0; i < gap; ++i) newMarkers.push(gapMarkers) newMarkers.push(last) } return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (let i = 0; i < spans.length; ++i) { let span = spans[i] if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) spans.splice(i--, 1) } if (!spans.length) return null return spans } // Used to 'clip' out readOnly ranges when making a change. export function removeReadOnlyRanges(doc, from, to) { let markers = null doc.iter(from.line, to.line + 1, line => { if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { let mark = line.markedSpans[i].marker if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) (markers || (markers = [])).push(mark) } }) if (!markers) return null let parts = [{from: from, to: to}] for (let i = 0; i < markers.length; ++i) { let mk = markers[i], m = mk.find(0) for (let j = 0; j < parts.length; ++j) { let p = parts[j] if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue let newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) newParts.push({from: p.from, to: m.from}) if (dto > 0 || !mk.inclusiveRight && !dto) newParts.push({from: m.to, to: p.to}) parts.splice.apply(parts, newParts) j += newParts.length - 3 } } return parts } // Connect or disconnect spans from a line. export function detachMarkedSpans(line) { let spans = line.markedSpans if (!spans) return for (let i = 0; i < spans.length; ++i) spans[i].marker.detachLine(line) line.markedSpans = null } export function attachMarkedSpans(line, spans) { if (!spans) return for (let i = 0; i < spans.length; ++i) spans[i].marker.attachLine(line) line.markedSpans = spans } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. export function compareCollapsedMarkers(a, b) { let lenDiff = a.lines.length - b.lines.length if (lenDiff != 0) return lenDiff let aPos = a.find(), bPos = b.find() let fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) if (fromCmp) return -fromCmp let toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) if (toCmp) return toCmp return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { let sps = sawCollapsedSpans && line.markedSpans, found if (sps) for (let sp, i = 0; i < sps.length; ++i) { sp = sps[i] if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker } return found } export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } export function collapsedSpanAround(line, ch) { let sps = sawCollapsedSpans && line.markedSpans, found if (sps) for (let i = 0; i < sps.length; ++i) { let sp = sps[i] if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) found = sp.marker } return found } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { let line = getLine(doc, lineNo) let sps = sawCollapsedSpans && line.markedSpans if (sps) for (let i = 0; i < sps.length; ++i) { let sp = sps[i] if (!sp.marker.collapsed) continue let found = sp.marker.find(0) let fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) let toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) return true } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). export function visualLine(line) { let merged while (merged = collapsedSpanAtStart(line)) line = merged.find(-1, true).line return line } export function visualLineEnd(line) { let merged while (merged = collapsedSpanAtEnd(line)) line = merged.find(1, true).line return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. export function visualLineContinued(line) { let merged, lines while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line) } return lines } // Get the line number of the start of the visual line that the // given line number is part of. export function visualLineNo(doc, lineN) { let line = getLine(doc, lineN), vis = visualLine(line) if (line == vis) return lineN return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. export function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) return lineN let line = getLine(doc, lineN), merged if (!lineIsHidden(doc, line)) return lineN while (merged = collapsedSpanAtEnd(line)) line = merged.find(1, true).line return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. export function lineIsHidden(doc, line) { let sps = sawCollapsedSpans && line.markedSpans if (sps) for (let sp, i = 0; i < sps.length; ++i) { sp = sps[i] if (!sp.marker.collapsed) continue if (sp.from == null) return true if (sp.marker.widgetNode) continue if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) return true } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { let end = span.marker.find(1, true) return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) return true for (let sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i] if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) return true } } // Find the height above the given line. export function heightAtLine(lineObj) { lineObj = visualLine(lineObj) let h = 0, chunk = lineObj.parent for (let i = 0; i < chunk.lines.length; ++i) { let line = chunk.lines[i] if (line == lineObj) break else h += line.height } for (let p = chunk.parent; p; chunk = p, p = chunk.parent) { for (let i = 0; i < p.children.length; ++i) { let cur = p.children[i] if (cur == chunk) break else h += cur.height } } return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. export function lineLength(line) { if (line.height == 0) return 0 let len = line.text.length, merged, cur = line while (merged = collapsedSpanAtStart(cur)) { let found = merged.find(0, true) cur = found.from.line len += found.from.ch - found.to.ch } cur = line while (merged = collapsedSpanAtEnd(cur)) { let found = merged.find(0, true) len -= cur.text.length - found.from.ch cur = found.to.line len += cur.text.length - found.to.ch } return len } // Find the longest line in the document. export function findMaxLine(cm) { let d = cm.display, doc = cm.doc d.maxLine = getLine(doc, doc.first) d.maxLineLength = lineLength(d.maxLine) d.maxLineChanged = true doc.iter(line => { let len = lineLength(line) if (len > d.maxLineLength) { d.maxLineLength = len d.maxLine = line } }) } line_data.js 0000644 00000033742 15030043631 0007026 0 ustar 00 import { getOrder } from "../util/bidi.js" import { ie, ie_version, webkit } from "../util/browser.js" import { elt, eltP, joinClasses } from "../util/dom.js" import { eventMixin, signal } from "../util/event.js" import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js" import { lst, spaceStr } from "../util/misc.js" import { getLineStyles } from "./highlight.js" import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js" import { getLine, lineNo, updateLineHeight } from "./utils_line.js" // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). export class Line { constructor(text, markedSpans, estimateHeight) { this.text = text attachMarkedSpans(this, markedSpans) this.height = estimateHeight ? estimateHeight(this) : 1 } lineNo() { return lineNo(this) } } eventMixin(Line) // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. export function updateLine(line, text, markedSpans, estimateHeight) { line.text = text if (line.stateAfter) line.stateAfter = null if (line.styles) line.styles = null if (line.order != null) line.order = null detachMarkedSpans(line) attachMarkedSpans(line, markedSpans) let estHeight = estimateHeight ? estimateHeight(line) : 1 if (estHeight != line.height) updateLineHeight(line, estHeight) } // Detach a line from the document tree and its markers. export function cleanUpLine(line) { line.parent = null detachMarkedSpans(line) } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. let styleToClassCache = {}, styleToClassCacheWithMode = {} function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) return null let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. export function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). let content = eltP("span", null, null, webkit ? "padding-right: .1px" : null) let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: cm.getOption("lineWrapping")} lineView.measure = {} // Iterate over the logical lines that make up this visual line. for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { let line = i ? lineView.rest[i - 1] : lineView.line, order builder.pos = 0 builder.addToken = buildToken // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) builder.addToken = buildTokenBadBidi(builder.addToken, order) builder.map = [] let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) if (line.styleClasses) { if (line.styleClasses.bgClass) builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") if (line.styleClasses.textClass) builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map lineView.measure.cache = {} } else { ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) } } // See issue #2901 if (webkit) { let last = builder.content.lastChild if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) builder.content.className = "cm-tab-wrap-hack" } signal(cm, "renderLine", cm, lineView.line, builder.pre) if (builder.pre.className) builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") return builder } export function defaultSpecialCharPlaceholder(ch) { let token = elt("span", "\u2022", "cm-invalidchar") token.title = "\\u" + ch.charCodeAt(0).toString(16) token.setAttribute("aria-label", token.title) return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { if (!text) return let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text let special = builder.cm.state.specialChars, mustWrap = false let content if (!special.test(text)) { builder.col += text.length content = document.createTextNode(displayText) builder.map.push(builder.pos, builder.pos + text.length, content) if (ie && ie_version < 9) mustWrap = true builder.pos += text.length } else { content = document.createDocumentFragment() let pos = 0 while (true) { special.lastIndex = pos let m = special.exec(text) let skipped = m ? m.index - pos : text.length - pos if (skipped) { let txt = document.createTextNode(displayText.slice(pos, pos + skipped)) if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) else content.appendChild(txt) builder.map.push(builder.pos, builder.pos + skipped, txt) builder.col += skipped builder.pos += skipped } if (!m) break pos += skipped + 1 let txt if (m[0] == "\t") { let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) txt.setAttribute("role", "presentation") txt.setAttribute("cm-text", "\t") builder.col += tabWidth } else if (m[0] == "\r" || m[0] == "\n") { txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) txt.setAttribute("cm-text", m[0]) builder.col += 1 } else { txt = builder.cm.options.specialCharPlaceholder(m[0]) txt.setAttribute("cm-text", m[0]) if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) else content.appendChild(txt) builder.col += 1 } builder.map.push(builder.pos, builder.pos + 1, txt) builder.pos++ } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 if (style || startStyle || endStyle || mustWrap || css || attributes) { let fullStyle = style || "" if (startStyle) fullStyle += startStyle if (endStyle) fullStyle += endStyle let token = elt("span", [content], fullStyle, css) if (attributes) { for (let attr in attributes) if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") token.setAttribute(attr, attributes[attr]) } return builder.content.appendChild(token) } builder.content.appendChild(content) } // Change some spaces to NBSP to prevent the browser from collapsing // trailing spaces at the end of a line when rendering text (issue #1362). function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) return text let spaceBefore = trailingBefore, result = "" for (let i = 0; i < text.length; i++) { let ch = text.charAt(i) if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) ch = "\u00a0" result += ch spaceBefore = ch == " " } return result } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return (builder, text, style, startStyle, endStyle, css, attributes) => { style = style ? style + " cm-force-border" : "cm-force-border" let start = builder.pos, end = start + text.length for (;;) { // Find the part that overlaps with the start of this text let part for (let i = 0; i < order.length; i++) { part = order[i] if (part.to > start && part.from <= start) break } if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, css, attributes) inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes) startStyle = null text = text.slice(part.to - start) start = part.to } } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { let widget = !ignoreWidget && marker.widgetNode if (widget) builder.map.push(builder.pos, builder.pos + size, widget) if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) widget = builder.content.appendChild(document.createElement("span")) widget.setAttribute("cm-marker", marker.id) } if (widget) { builder.cm.display.input.setUneditable(widget) builder.content.appendChild(widget) } builder.pos += size builder.trailingSpace = false } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { let spans = line.markedSpans, allText = line.text, at = 0 if (!spans) { for (let i = 1; i < styles.length; i+=2) builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) return } let len = allText.length, pos = 0, i = 1, text = "", style, css let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = css = "" attributes = null collapsed = null; nextChange = Infinity let foundBookmarks = [], endStyles for (let j = 0; j < spans.length; ++j) { let sp = spans[j], m = sp.marker if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m) } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to spanEndStyle = "" } if (m.className) spanStyle += " " + m.className if (m.css) css = (css ? css + ";" : "") + m.css if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) // support for the old title property // https://github.com/codemirror/CodeMirror/pull/5673 if (m.title) (attributes || (attributes = {})).title = m.title if (m.attributes) { for (let attr in m.attributes) (attributes || (attributes = {}))[attr] = m.attributes[attr] } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from } } if (endStyles) for (let j = 0; j < endStyles.length; j += 2) if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j) buildCollapsedSpan(builder, 0, foundBookmarks[j]) if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null) if (collapsed.to == null) return if (collapsed.to == pos) collapsed = false } } if (pos >= len) break let upto = Math.min(len, nextChange) while (true) { if (text) { let end = pos + text.length if (!collapsed) { let tokenText = end > upto ? text.slice(0, upto - pos) : text builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes) } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end spanStartStyle = "" } text = allText.slice(at, at = styles[i++]) style = interpretTokenStyle(styles[i++], builder.cm.options) } } } // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. export function LineView(doc, line, lineN) { // The starting line this.line = line // Continuing lines, if any this.rest = visualLineContinued(line) // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1 this.node = this.text = null this.hidden = lineIsHidden(doc, line) } // Create a range of LineView objects for the given lines. export function buildViewArray(cm, from, to) { let array = [], nextPos for (let pos = from; pos < to; pos = nextPos) { let view = new LineView(cm.doc, getLine(cm.doc, pos), pos) nextPos = pos + view.size array.push(view) } return array }
| ver. 1.4 |
Github
|
.
| PHP 8.2.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings