mirror of
https://github.com/atom/atom.git
synced 2026-02-15 17:15:24 -05:00
Merge branch 'master' into mb-use-language-mode-api
This commit is contained in:
@@ -32,6 +32,7 @@ ThemeManager = require './theme-manager'
|
||||
MenuManager = require './menu-manager'
|
||||
ContextMenuManager = require './context-menu-manager'
|
||||
CommandInstaller = require './command-installer'
|
||||
CoreURIHandlers = require './core-uri-handlers'
|
||||
ProtocolHandlerInstaller = require './protocol-handler-installer'
|
||||
Project = require './project'
|
||||
TitleBar = require './title-bar'
|
||||
@@ -240,6 +241,7 @@ class AtomEnvironment extends Model
|
||||
|
||||
@commandInstaller.initialize(@getVersion())
|
||||
@protocolHandlerInstaller.initialize(@config, @notifications)
|
||||
@uriHandlerRegistry.registerHostHandler('core', CoreURIHandlers.create(this))
|
||||
@autoUpdater.initialize()
|
||||
|
||||
@config.load()
|
||||
|
||||
@@ -107,6 +107,13 @@ module.exports = class CommandRegistry {
|
||||
// otherwise be generated from the event name.
|
||||
// * `description`: Used by consumers to display detailed information about
|
||||
// the command.
|
||||
// * `hiddenInCommandPalette`: If `true`, this command will not appear in
|
||||
// the bundled command palette by default, but can still be shown with.
|
||||
// the `Command Palette: Show Hidden Commands` command. This is a good
|
||||
// option when you need to register large numbers of commands that don't
|
||||
// make sense to be executed from the command palette. Please use this
|
||||
// option conservatively, as it could reduce the discoverability of your
|
||||
// package's commands.
|
||||
//
|
||||
// ## Arguments: Registering Multiple Commands
|
||||
//
|
||||
|
||||
38
src/core-uri-handlers.js
Normal file
38
src/core-uri-handlers.js
Normal file
@@ -0,0 +1,38 @@
|
||||
function openFile (atom, {query}) {
|
||||
const {filename, line, column} = query
|
||||
|
||||
atom.workspace.open(filename, {
|
||||
initialLine: parseInt(line || 0, 10),
|
||||
initialColumn: parseInt(column || 0, 10),
|
||||
searchAllPanes: true
|
||||
})
|
||||
}
|
||||
|
||||
function windowShouldOpenFile ({query}) {
|
||||
const {filename} = query
|
||||
return (win) => win.containsPath(filename)
|
||||
}
|
||||
|
||||
const ROUTER = {
|
||||
'/open/file': { handler: openFile, getWindowPredicate: windowShouldOpenFile }
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create (atomEnv) {
|
||||
return function coreURIHandler (parsed) {
|
||||
const config = ROUTER[parsed.pathname]
|
||||
if (config) {
|
||||
config.handler(atomEnv, parsed)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
windowPredicate (parsed) {
|
||||
const config = ROUTER[parsed.pathname]
|
||||
if (config && config.getWindowPredicate) {
|
||||
return config.getWindowPredicate(parsed)
|
||||
} else {
|
||||
return (win) => true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,7 +594,7 @@ class Cursor extends Model {
|
||||
getCurrentWordBufferRange (options = {}) {
|
||||
const position = this.getBufferPosition()
|
||||
const ranges = this.editor.buffer.findAllInRangeSync(
|
||||
options.wordRegex || this.wordRegExp(),
|
||||
options.wordRegex || this.wordRegExp(options),
|
||||
new Range(new Point(position.row, 0), new Point(position.row, Infinity))
|
||||
)
|
||||
const range = ranges.find(range =>
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* DS104: Avoid inline assignments
|
||||
* DS207: Consider shorter variations of null checks
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const {join} = require('path')
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
const _ = require('underscore-plus')
|
||||
const {Emitter, Disposable, CompositeDisposable} = require('event-kit')
|
||||
const fs = require('fs-plus')
|
||||
const path = require('path')
|
||||
const GitUtils = require('git-utils')
|
||||
|
||||
let nextId = 0
|
||||
@@ -241,15 +233,15 @@ class GitRepository {
|
||||
// * `path` The {String} path to check.
|
||||
//
|
||||
// Returns a {Boolean}.
|
||||
isSubmodule (path) {
|
||||
if (!path) return false
|
||||
isSubmodule (filePath) {
|
||||
if (!filePath) return false
|
||||
|
||||
const repo = this.getRepo(path)
|
||||
if (repo.isSubmodule(repo.relativize(path))) {
|
||||
const repo = this.getRepo(filePath)
|
||||
if (repo.isSubmodule(repo.relativize(filePath))) {
|
||||
return true
|
||||
} else {
|
||||
// Check if the path is a working directory in a repo that isn't the root.
|
||||
return repo !== this.getRepo() && repo.relativize(join(path, 'dir')) === 'dir'
|
||||
// Check if the filePath is a working directory in a repo that isn't the root.
|
||||
return repo !== this.getRepo() && repo.relativize(path.join(filePath, 'dir')) === 'dir'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class ApplicationMenu
|
||||
]
|
||||
|
||||
focusedWindow: ->
|
||||
_.find global.atomApplication.windows, (atomWindow) -> atomWindow.isFocused()
|
||||
_.find global.atomApplication.getAllWindows(), (atomWindow) -> atomWindow.isFocused()
|
||||
|
||||
# Combines a menu template with the appropriate keystroke.
|
||||
#
|
||||
|
||||
@@ -67,7 +67,7 @@ class AtomApplication
|
||||
{@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @userDataDir} = options
|
||||
@socketPath = null if options.test or options.benchmark or options.benchmarkTest
|
||||
@pidsToOpenWindows = {}
|
||||
@windows = []
|
||||
@windowStack = new WindowStack()
|
||||
|
||||
@config = new Config({enablePersistence: true})
|
||||
@config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)}
|
||||
@@ -114,7 +114,7 @@ class AtomApplication
|
||||
@launch(options)
|
||||
|
||||
destroy: ->
|
||||
windowsClosePromises = @windows.map (window) ->
|
||||
windowsClosePromises = @getAllWindows().map (window) ->
|
||||
window.close()
|
||||
window.closedPromise
|
||||
Promise.all(windowsClosePromises).then(=> @disposable.dispose())
|
||||
@@ -162,8 +162,8 @@ class AtomApplication
|
||||
|
||||
# Public: Removes the {AtomWindow} from the global window list.
|
||||
removeWindow: (window) ->
|
||||
@windows.splice(@windows.indexOf(window), 1)
|
||||
if @windows.length is 0
|
||||
@windowStack.removeWindow(window)
|
||||
if @getAllWindows().length is 0
|
||||
@applicationMenu?.enableWindowSpecificItems(false)
|
||||
if process.platform in ['win32', 'linux']
|
||||
app.quit()
|
||||
@@ -172,22 +172,28 @@ class AtomApplication
|
||||
|
||||
# Public: Adds the {AtomWindow} to the global window list.
|
||||
addWindow: (window) ->
|
||||
@windows.push window
|
||||
@windowStack.addWindow(window)
|
||||
@applicationMenu?.addWindow(window.browserWindow)
|
||||
window.once 'window:loaded', =>
|
||||
@autoUpdateManager?.emitUpdateAvailableEvent(window)
|
||||
|
||||
unless window.isSpec
|
||||
focusHandler = => @lastFocusedWindow = window
|
||||
focusHandler = => @windowStack.touch(window)
|
||||
blurHandler = => @saveState(false)
|
||||
window.browserWindow.on 'focus', focusHandler
|
||||
window.browserWindow.on 'blur', blurHandler
|
||||
window.browserWindow.once 'closed', =>
|
||||
@lastFocusedWindow = null if window is @lastFocusedWindow
|
||||
@windowStack.removeWindow(window)
|
||||
window.browserWindow.removeListener 'focus', focusHandler
|
||||
window.browserWindow.removeListener 'blur', blurHandler
|
||||
window.browserWindow.webContents.once 'did-finish-load', => @saveState(false)
|
||||
|
||||
getAllWindows: =>
|
||||
@windowStack.all().slice()
|
||||
|
||||
getLastFocusedWindow: (predicate) =>
|
||||
@windowStack.getLastFocusedWindow(predicate)
|
||||
|
||||
# Creates server to listen for additional atom application launches.
|
||||
#
|
||||
# You can run the atom command multiple times, but after the first launch
|
||||
@@ -276,7 +282,7 @@ class AtomApplication
|
||||
else
|
||||
event.preventDefault()
|
||||
@quitting = true
|
||||
windowUnloadPromises = @windows.map((window) -> window.prepareToUnload())
|
||||
windowUnloadPromises = @getAllWindows().map((window) -> window.prepareToUnload())
|
||||
Promise.all(windowUnloadPromises).then((windowUnloadedResults) ->
|
||||
didUnloadAllWindows = windowUnloadedResults.every((didUnloadWindow) -> didUnloadWindow)
|
||||
app.quit() if didUnloadAllWindows
|
||||
@@ -309,7 +315,7 @@ class AtomApplication
|
||||
event.sender.send('did-resolve-proxy', requestId, proxy)
|
||||
|
||||
@disposable.add ipcHelpers.on ipcMain, 'did-change-history-manager', (event) =>
|
||||
for atomWindow in @windows
|
||||
for atomWindow in @getAllWindows()
|
||||
webContents = atomWindow.browserWindow.webContents
|
||||
if webContents isnt event.sender
|
||||
webContents.send('did-change-history-manager')
|
||||
@@ -483,7 +489,7 @@ class AtomApplication
|
||||
|
||||
# Returns the {AtomWindow} for the given paths.
|
||||
windowForPaths: (pathsToOpen, devMode) ->
|
||||
_.find @windows, (atomWindow) ->
|
||||
_.find @getAllWindows(), (atomWindow) ->
|
||||
atomWindow.devMode is devMode and atomWindow.containsPaths(pathsToOpen)
|
||||
|
||||
# Returns the {AtomWindow} for the given ipcMain event.
|
||||
@@ -491,11 +497,11 @@ class AtomApplication
|
||||
@atomWindowForBrowserWindow(BrowserWindow.fromWebContents(sender))
|
||||
|
||||
atomWindowForBrowserWindow: (browserWindow) ->
|
||||
@windows.find((atomWindow) -> atomWindow.browserWindow is browserWindow)
|
||||
@getAllWindows().find((atomWindow) -> atomWindow.browserWindow is browserWindow)
|
||||
|
||||
# Public: Returns the currently focused {AtomWindow} or undefined if none.
|
||||
focusedWindow: ->
|
||||
_.find @windows, (atomWindow) -> atomWindow.isFocused()
|
||||
_.find @getAllWindows(), (atomWindow) -> atomWindow.isFocused()
|
||||
|
||||
# Get the platform-specific window offset for new windows.
|
||||
getWindowOffsetForCurrentPlatform: ->
|
||||
@@ -507,8 +513,8 @@ class AtomApplication
|
||||
# Get the dimensions for opening a new window by cascading as appropriate to
|
||||
# the platform.
|
||||
getDimensionsForNewWindow: ->
|
||||
return if (@focusedWindow() ? @lastFocusedWindow)?.isMaximized()
|
||||
dimensions = (@focusedWindow() ? @lastFocusedWindow)?.getDimensions()
|
||||
return if (@focusedWindow() ? @getLastFocusedWindow())?.isMaximized()
|
||||
dimensions = (@focusedWindow() ? @getLastFocusedWindow())?.getDimensions()
|
||||
offset = @getWindowOffsetForCurrentPlatform()
|
||||
if dimensions? and offset?
|
||||
dimensions.x += offset
|
||||
@@ -554,7 +560,7 @@ class AtomApplication
|
||||
existingWindow = @windowForPaths(pathsToOpen, devMode)
|
||||
stats = (fs.statSyncNoException(pathToOpen) for pathToOpen in pathsToOpen)
|
||||
unless existingWindow?
|
||||
if currentWindow = window ? @lastFocusedWindow
|
||||
if currentWindow = window ? @getLastFocusedWindow()
|
||||
existingWindow = currentWindow if (
|
||||
addToLastWindow or
|
||||
currentWindow.devMode is devMode and
|
||||
@@ -583,7 +589,7 @@ class AtomApplication
|
||||
windowDimensions ?= @getDimensionsForNewWindow()
|
||||
openedWindow = new AtomWindow(this, @fileRecoveryService, {initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState, env})
|
||||
openedWindow.focus()
|
||||
@lastFocusedWindow = openedWindow
|
||||
@windowStack.addWindow(openedWindow)
|
||||
|
||||
if pidToKillWhenClosed?
|
||||
@pidsToOpenWindows[pidToKillWhenClosed] = openedWindow
|
||||
@@ -617,9 +623,10 @@ class AtomApplication
|
||||
saveState: (allowEmpty=false) ->
|
||||
return if @quitting
|
||||
states = []
|
||||
for window in @windows
|
||||
for window in @getAllWindows()
|
||||
unless window.isSpec
|
||||
states.push({initialPaths: window.representedDirectoryPaths})
|
||||
states.reverse()
|
||||
if states.length > 0 or allowEmpty
|
||||
@storageFolder.storeSync('application.json', states)
|
||||
@emit('application:did-save-state')
|
||||
@@ -648,30 +655,39 @@ class AtomApplication
|
||||
# :devMode - Boolean to control the opened window's dev mode.
|
||||
# :safeMode - Boolean to control the opened window's safe mode.
|
||||
openUrl: ({urlToOpen, devMode, safeMode, env}) ->
|
||||
parsedUrl = url.parse(urlToOpen)
|
||||
parsedUrl = url.parse(urlToOpen, true)
|
||||
return unless parsedUrl.protocol is "atom:"
|
||||
|
||||
pack = @findPackageWithName(parsedUrl.host, devMode)
|
||||
if pack?.urlMain
|
||||
@openPackageUrlMain(parsedUrl.host, pack.urlMain, urlToOpen, devMode, safeMode, env)
|
||||
else
|
||||
@openPackageUriHandler(urlToOpen, devMode, safeMode, env)
|
||||
@openPackageUriHandler(urlToOpen, parsedUrl, devMode, safeMode, env)
|
||||
|
||||
openPackageUriHandler: (url, devMode, safeMode, env) ->
|
||||
resourcePath = @resourcePath
|
||||
if devMode
|
||||
try
|
||||
windowInitializationScript = require.resolve(path.join(@devResourcePath, 'src', 'initialize-application-window'))
|
||||
resourcePath = @devResourcePath
|
||||
openPackageUriHandler: (url, parsedUrl, devMode, safeMode, env) ->
|
||||
bestWindow = null
|
||||
if parsedUrl.host is 'core'
|
||||
predicate = require('../core-uri-handlers').windowPredicate(parsedUrl)
|
||||
bestWindow = @getLastFocusedWindow (win) ->
|
||||
not win.isSpecWindow() and predicate(win)
|
||||
|
||||
windowInitializationScript ?= require.resolve('../initialize-application-window')
|
||||
if @lastFocusedWindow?
|
||||
@lastFocusedWindow.sendURIMessage url
|
||||
bestWindow ?= @getLastFocusedWindow (win) -> not win.isSpecWindow()
|
||||
if bestWindow?
|
||||
bestWindow.sendURIMessage url
|
||||
bestWindow.focus()
|
||||
else
|
||||
resourcePath = @resourcePath
|
||||
if devMode
|
||||
try
|
||||
windowInitializationScript = require.resolve(path.join(@devResourcePath, 'src', 'initialize-application-window'))
|
||||
resourcePath = @devResourcePath
|
||||
|
||||
windowInitializationScript ?= require.resolve('../initialize-application-window')
|
||||
windowDimensions = @getDimensionsForNewWindow()
|
||||
@lastFocusedWindow = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env})
|
||||
@lastFocusedWindow.on 'window:loaded', =>
|
||||
@lastFocusedWindow.sendURIMessage url
|
||||
win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env})
|
||||
@windowStack.addWindow(win)
|
||||
win.on 'window:loaded', ->
|
||||
win.sendURIMessage url
|
||||
|
||||
findPackageWithName: (packageName, devMode) ->
|
||||
_.find @getPackageManager(devMode).getAvailablePackageMetadata(), ({name}) -> name is packageName
|
||||
@@ -867,7 +883,7 @@ class AtomApplication
|
||||
|
||||
disableZoomOnDisplayChange: ->
|
||||
outerCallback = =>
|
||||
for window in @windows
|
||||
for window in @getAllWindows()
|
||||
window.disableZoom()
|
||||
|
||||
# Set the limits every time a display is added or removed, otherwise the
|
||||
@@ -878,3 +894,24 @@ class AtomApplication
|
||||
new Disposable ->
|
||||
screen.removeListener('display-added', outerCallback)
|
||||
screen.removeListener('display-removed', outerCallback)
|
||||
|
||||
class WindowStack
|
||||
constructor: (@windows = []) ->
|
||||
|
||||
addWindow: (window) =>
|
||||
@removeWindow(window)
|
||||
@windows.unshift(window)
|
||||
|
||||
touch: (window) =>
|
||||
@addWindow(window)
|
||||
|
||||
removeWindow: (window) =>
|
||||
currentIndex = @windows.indexOf(window)
|
||||
@windows.splice(currentIndex, 1) if currentIndex > -1
|
||||
|
||||
getLastFocusedWindow: (predicate) =>
|
||||
predicate ?= (win) -> true
|
||||
@windows.find(predicate)
|
||||
|
||||
all: =>
|
||||
@windows
|
||||
|
||||
@@ -138,4 +138,4 @@ class AutoUpdateManager
|
||||
detail: message
|
||||
|
||||
getWindows: ->
|
||||
global.atomApplication.windows
|
||||
global.atomApplication.getAllWindows()
|
||||
|
||||
@@ -341,13 +341,21 @@ class Project extends Model {
|
||||
}
|
||||
|
||||
this.rootDirectories.push(directory)
|
||||
this.watcherPromisesByPath[directory.getPath()] = watchPath(directory.getPath(), {}, events => {
|
||||
|
||||
const didChangeCallback = events => {
|
||||
// Stop event delivery immediately on removal of a rootDirectory, even if its watcher
|
||||
// promise has yet to resolve at the time of removal
|
||||
if (this.rootDirectories.includes(directory)) {
|
||||
this.emitter.emit('did-change-files', events)
|
||||
}
|
||||
})
|
||||
}
|
||||
// We'll use the directory's custom onDidChangeFiles callback, if available.
|
||||
// CustomDirectory::onDidChangeFiles should match the signature of
|
||||
// Project::onDidChangeFiles below (although it may resolve asynchronously)
|
||||
this.watcherPromisesByPath[directory.getPath()] =
|
||||
directory.onDidChangeFiles != null
|
||||
? Promise.resolve(directory.onDidChangeFiles(didChangeCallback))
|
||||
: watchPath(directory.getPath(), {}, didChangeCallback)
|
||||
|
||||
for (let watchedPath in this.watcherPromisesByPath) {
|
||||
if (!this.rootDirectories.find(dir => dir.getPath() === watchedPath)) {
|
||||
|
||||
@@ -126,7 +126,6 @@ class TextEditorComponent {
|
||||
this.blockDecorationResizeObserver = new ResizeObserver(this.didResizeBlockDecorations.bind(this))
|
||||
this.lineComponentsByScreenLineId = new Map()
|
||||
this.overlayComponents = new Set()
|
||||
this.overlayDimensionsByElement = new WeakMap()
|
||||
this.shouldRenderDummyScrollbars = true
|
||||
this.remeasureScrollbars = false
|
||||
this.pendingAutoscroll = null
|
||||
@@ -803,15 +802,9 @@ class TextEditorComponent {
|
||||
{
|
||||
key: overlayProps.element,
|
||||
overlayComponents: this.overlayComponents,
|
||||
measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element),
|
||||
didResize: (overlayComponent) => {
|
||||
this.updateOverlayToRender(overlayProps)
|
||||
overlayComponent.update(Object.assign(
|
||||
{
|
||||
measuredDimensions: this.overlayDimensionsByElement.get(overlayProps.element)
|
||||
},
|
||||
overlayProps
|
||||
))
|
||||
overlayComponent.update(overlayProps)
|
||||
}
|
||||
},
|
||||
overlayProps
|
||||
@@ -1357,7 +1350,6 @@ class TextEditorComponent {
|
||||
let wrapperTop = contentClientRect.top + this.pixelPositionAfterBlocksForRow(row) + this.getLineHeight()
|
||||
let wrapperLeft = contentClientRect.left + this.pixelLeftForRowAndColumn(row, column)
|
||||
const clientRect = element.getBoundingClientRect()
|
||||
this.overlayDimensionsByElement.set(element, clientRect)
|
||||
|
||||
if (avoidOverflow !== false) {
|
||||
const computedStyle = window.getComputedStyle(element)
|
||||
@@ -4226,17 +4218,26 @@ class OverlayComponent {
|
||||
this.element.style.zIndex = 4
|
||||
this.element.style.top = (this.props.pixelTop || 0) + 'px'
|
||||
this.element.style.left = (this.props.pixelLeft || 0) + 'px'
|
||||
this.currentContentRect = null
|
||||
|
||||
// Synchronous DOM updates in response to resize events might trigger a
|
||||
// "loop limit exceeded" error. We disconnect the observer before
|
||||
// potentially mutating the DOM, and then reconnect it on the next tick.
|
||||
// Note: ResizeObserver calls its callback when .observe is called
|
||||
this.resizeObserver = new ResizeObserver((entries) => {
|
||||
const {contentRect} = entries[0]
|
||||
if (contentRect.width !== this.props.measuredDimensions.width || contentRect.height !== this.props.measuredDimensions.height) {
|
||||
|
||||
if (
|
||||
this.currentContentRect &&
|
||||
(this.currentContentRect.width !== contentRect.width ||
|
||||
this.currentContentRect.height !== contentRect.height)
|
||||
) {
|
||||
this.resizeObserver.disconnect()
|
||||
this.props.didResize(this)
|
||||
process.nextTick(() => { this.resizeObserver.observe(this.props.element) })
|
||||
}
|
||||
|
||||
this.currentContentRect = contentRect
|
||||
})
|
||||
this.didAttach()
|
||||
this.props.overlayComponents.add(this)
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
module.exports =
|
||||
class TokenIterator
|
||||
constructor: (@tokenizedBuffer) ->
|
||||
|
||||
reset: (@line) ->
|
||||
@index = null
|
||||
@startColumn = 0
|
||||
@endColumn = 0
|
||||
@scopes = @line.openScopes.map (id) => @tokenizedBuffer.grammar.scopeForId(id)
|
||||
@scopeStarts = @scopes.slice()
|
||||
@scopeEnds = []
|
||||
this
|
||||
|
||||
next: ->
|
||||
{tags} = @line
|
||||
|
||||
if @index?
|
||||
@startColumn = @endColumn
|
||||
@scopeEnds.length = 0
|
||||
@scopeStarts.length = 0
|
||||
@index++
|
||||
else
|
||||
@index = 0
|
||||
|
||||
while @index < tags.length
|
||||
tag = tags[@index]
|
||||
if tag < 0
|
||||
scope = @tokenizedBuffer.grammar.scopeForId(tag)
|
||||
if tag % 2 is 0
|
||||
if @scopeStarts[@scopeStarts.length - 1] is scope
|
||||
@scopeStarts.pop()
|
||||
else
|
||||
@scopeEnds.push(scope)
|
||||
@scopes.pop()
|
||||
else
|
||||
@scopeStarts.push(scope)
|
||||
@scopes.push(scope)
|
||||
@index++
|
||||
else
|
||||
@endColumn += tag
|
||||
@text = @line.text.substring(@startColumn, @endColumn)
|
||||
return true
|
||||
|
||||
false
|
||||
|
||||
getScopes: -> @scopes
|
||||
|
||||
getScopeStarts: -> @scopeStarts
|
||||
|
||||
getScopeEnds: -> @scopeEnds
|
||||
|
||||
getText: -> @text
|
||||
|
||||
getBufferStart: -> @startColumn
|
||||
|
||||
getBufferEnd: -> @endColumn
|
||||
79
src/token-iterator.js
Normal file
79
src/token-iterator.js
Normal file
@@ -0,0 +1,79 @@
|
||||
module.exports =
|
||||
class TokenIterator {
|
||||
constructor (tokenizedBuffer) {
|
||||
this.tokenizedBuffer = tokenizedBuffer
|
||||
}
|
||||
|
||||
reset (line) {
|
||||
this.line = line
|
||||
this.index = null
|
||||
this.startColumn = 0
|
||||
this.endColumn = 0
|
||||
this.scopes = this.line.openScopes.map(id => this.tokenizedBuffer.grammar.scopeForId(id))
|
||||
this.scopeStarts = this.scopes.slice()
|
||||
this.scopeEnds = []
|
||||
return this
|
||||
}
|
||||
|
||||
next () {
|
||||
const {tags} = this.line
|
||||
|
||||
if (this.index != null) {
|
||||
this.startColumn = this.endColumn
|
||||
this.scopeEnds.length = 0
|
||||
this.scopeStarts.length = 0
|
||||
this.index++
|
||||
} else {
|
||||
this.index = 0
|
||||
}
|
||||
|
||||
while (this.index < tags.length) {
|
||||
const tag = tags[this.index]
|
||||
if (tag < 0) {
|
||||
const scope = this.tokenizedBuffer.grammar.scopeForId(tag)
|
||||
if ((tag % 2) === 0) {
|
||||
if (this.scopeStarts[this.scopeStarts.length - 1] === scope) {
|
||||
this.scopeStarts.pop()
|
||||
} else {
|
||||
this.scopeEnds.push(scope)
|
||||
}
|
||||
this.scopes.pop()
|
||||
} else {
|
||||
this.scopeStarts.push(scope)
|
||||
this.scopes.push(scope)
|
||||
}
|
||||
this.index++
|
||||
} else {
|
||||
this.endColumn += tag
|
||||
this.text = this.line.text.substring(this.startColumn, this.endColumn)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
getScopes () {
|
||||
return this.scopes
|
||||
}
|
||||
|
||||
getScopeStarts () {
|
||||
return this.scopeStarts
|
||||
}
|
||||
|
||||
getScopeEnds () {
|
||||
return this.scopeEnds
|
||||
}
|
||||
|
||||
getText () {
|
||||
return this.text
|
||||
}
|
||||
|
||||
getBufferStart () {
|
||||
return this.startColumn
|
||||
}
|
||||
|
||||
getBufferEnd () {
|
||||
return this.endColumn
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user