import { join, resolve } from "path" import { existsSync } from "fs" import { Point, TextBuffer, TextEditor, Range, BufferScanResult } from "atom" import { CancellationToken, CancellationTokenSource } from "vscode-jsonrpc" export type ReportBusyWhile = <T>(title: string, f: () => Promise<T>) => Promise<T> /** Obtain the range of the word at the given editor position. Uses the non-word characters from the position's grammar scope. */ export function getWordAtPosition(editor: TextEditor, position: Point): Range { const nonWordCharacters = escapeRegExp(editor.getNonWordCharacters(position)) const range = _getRegexpRangeAtPosition( editor.getBuffer(), position, new RegExp(`^[\t ]*$|[^\\s${nonWordCharacters}]+`, "g") ) if (range == null) { return new Range(position, position) } return range } export function escapeRegExp(string: string): string { // From atom/underscore-plus. return string.replace(/[$()*+./?[\\\]^{|}-]/g, "\\$&") } function _getRegexpRangeAtPosition(buffer: TextBuffer, position: Point, wordRegex: RegExp): Range | null { const { row, column } = position const rowRange = buffer.rangeForRow(row, false) let matchData: BufferScanResult | undefined | null // Extract the expression from the row text. buffer.scanInRange(wordRegex, rowRange, (data) => { const { range } = data if ( position.isGreaterThanOrEqual(range.start) && // Range endpoints are exclusive. position.isLessThan(range.end) ) { matchData = data data.stop() return } // Stop the scan if the scanner has passed our position. if (range.end.column > column) { data.stop() } }) return matchData == null ? null : matchData.range } /** * For the given connection and cancellationTokens map, cancel the existing CancellationToken for that connection then * create and store a new CancellationToken to be used for the current request. */ export function cancelAndRefreshCancellationToken<T extends object>( key: T, cancellationTokens: WeakMap<T, CancellationTokenSource> ): CancellationToken { let cancellationToken = cancellationTokens.get(key) if (cancellationToken !== undefined && !cancellationToken.token.isCancellationRequested) { cancellationToken.cancel() } cancellationToken = new CancellationTokenSource() cancellationTokens.set(key, cancellationToken) return cancellationToken.token } export async function doWithCancellationToken<T1 extends object, T2>( key: T1, cancellationTokens: WeakMap<T1, CancellationTokenSource>, work: (token: CancellationToken) => Promise<T2> ): Promise<T2> { const token = cancelAndRefreshCancellationToken(key, cancellationTokens) const result: T2 = await work(token) cancellationTokens.delete(key) return result } export function assertUnreachable(_: never): never { return _ } export function promiseWithTimeout<T>(ms: number, promise: Promise<T>): Promise<T> { return new Promise((resolve, reject) => { // create a timeout to reject promise if not resolved const timer = setTimeout(() => { reject(new Error(`Timeout after ${ms}ms`)) }, ms) promise .then((res) => { clearTimeout(timer) resolve(res) }) .catch((err) => { clearTimeout(timer) reject(err) }) }) } export const rootPathDefault = join("bin", `${process.platform}-${process.arch}`) export const exeExtentionDefault = process.platform === "win32" ? ".exe" : "" /** * Finds an exe file in the package assuming it is placed under `rootPath/platform-arch/exe`. If the exe file did not * exist, the given name is returned. For example on Windows x64, if the `exeName` is `serve-d`, it returns the absolute * path to `./bin/win32-x64/exeName.exe`, and if the file did not exist, `serve-d` is returned. * * @param exeName Name of the exe file * @param rootPath The path of the folder of the exe file. Defaults to 'join("bin", `${process.platform}-${process.arch}`)' * @param exeExtention The extention of the exe file. Defaults to `process.platform === "win32" ? ".exe" : ""` */ export function getExePath(exeName: string, rootPath = rootPathDefault, exeExtention = exeExtentionDefault): string { const exePath = resolve(join(rootPath, `${exeName}${exeExtention}`)) if (existsSync(exePath)) { return exePath } else { return exeName } }