From 5529aff976ab6f3f03aea4f59e51b94adc9bc76f Mon Sep 17 00:00:00 2001 From: aviatesk Date: Tue, 29 Oct 2019 14:08:02 +0900 Subject: [PATCH 1/5] strip trailing dots --- lib/misc/words.js | 31 +++++++++++++++------- lib/runtime/datatip.js | 29 ++++++++++++++------ lib/runtime/evaluation.coffee | 4 +-- lib/runtime/goto.js | 50 +++++++++++++++++++++++++++-------- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/lib/misc/words.js b/lib/misc/words.js index fbb6ca82..0b848ad2 100644 --- a/lib/misc/words.js +++ b/lib/misc/words.js @@ -3,13 +3,14 @@ import { Point, Range } from 'atom' export const wordRegex = /[\u00A0-\uFFFF\w_!´\.]*@?[\u00A0-\uFFFF\w_!´]+/ +export const wordRegexWithoutDotAccessor = /@?[\u00A0-\uFFFF\w_!´]+/ /** * Takes an `editor` and gets the word at current cursor position. If that is nonempty, call * function `fn` with arguments `word` and `range`. */ export function withWord (editor, fn) { - const { word, range } = getWord(editor) + const { word, range } = getWordAndRange(editor) // If we only find numbers or nothing, return prematurely if (!isValidWordToInspect(word)) return fn(word, range) @@ -18,17 +19,26 @@ export function withWord (editor, fn) { /** * Gets the word and its range in the `editor` * - * `bufferPosition` {Point}: If given returns the word at the `bufferPosition`, returns the word at the current cursor otherwise. + * `options` + * - `bufferPosition` {Point}: If given returns the word at the `bufferPosition`, returns the word at the current cursor otherwise. + * - `wordRegex` {RegExp} : A RegExp indicating what constitutes a “word” (default: `wordRegex`). */ -export function getWord (editor, bufferPosition) { +export function getWordAndRange (editor, options = { + bufferPosition: undefined, + wordRegex: wordRegex +}) { // @TODO?: // The following lines are kinda iffy: The regex may or may not be well chosen // and it duplicates the efforts from atom-language-julia. // It might be better to select the current word via finding the smallest // containing the bufferPosition/cursor which also has `function` or `macro` as its class. - const range = bufferPosition ? - getWordRangeAtBufferPosition(editor, bufferPosition) : - editor.getLastCursor().getCurrentWordBufferRange({ wordRegex }) + const range = options.bufferPosition ? + getWordRangeAtBufferPosition(editor, options.bufferPosition, { + wordRegex: options.wordRegex ? options.wordRegex : wordRegex + }) : + editor.getLastCursor().getCurrentWordBufferRange({ + wordRegex: options.wordRegex ? options.wordRegex : wordRegex + }) const word = editor.getTextInBufferRange(range) return { word, range } } @@ -37,12 +47,15 @@ export function getWord (editor, bufferPosition) { * get the word under `bufferPosition` in `editor` * adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616 * - * - optionalWordRegex: if not given, the toplevel `wordRegex` would be used + * `options` + * - `wordRegex` {RegExp}: A RegExp indicating what constitutes a “word” (default: `wordRegex`). */ -export function getWordRangeAtBufferPosition(editor, bufferPosition, optionalWordRegex) { +export function getWordRangeAtBufferPosition(editor, bufferPosition, options = { + wordRegex: wordRegex +}) { const { row, column } = bufferPosition const ranges = editor.getBuffer().findAllInRangeSync( - optionalWordRegex || wordRegex, + options.wordRegex ? options.wordRegex : wordRegex, new Range(new Point(row, 0), new Point(row, Infinity)) ) const range = ranges.find(range => diff --git a/lib/runtime/datatip.js b/lib/runtime/datatip.js index 0fab5f2d..60cda1f6 100644 --- a/lib/runtime/datatip.js +++ b/lib/runtime/datatip.js @@ -11,7 +11,11 @@ import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' -import { getWord, isValidWordToInspect } from '../misc/words' +import { + wordRegexWithoutDotAccessor, + getWordAndRange, + isValidWordToInspect +} from '../misc/words' import { getLocalContext } from '../misc/blocks' const datatip = client.import('datatip') @@ -34,9 +38,17 @@ class DatatipProvider { // If the scope at `bufferPosition` is not valid code scope, do nothing if (!isValidScopeToInspect(editor, bufferPosition)) return - // Check the validity of code to be inspected - const { range, word } = getWord(editor, bufferPosition) - if (!isValidWordToInspect(word)) return + // get word w/ w/o dots at the buffer position + const { range, word } = getWordAndRange(editor, { + bufferPosition, + wordRegex: wordRegexWithoutDotAccessor + }) + const { word: fullWord, range: fullRange } = getWordAndRange(editor, { + bufferPosition + }) + + // check the validity of code to be inspected + if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return const { main, sub } = await modules.getEditorModule(editor, bufferPosition) const mod = main ? (sub ? `${main}.${sub}` : main) : 'Main' @@ -47,6 +59,7 @@ class DatatipProvider { try { const result = await datatip({ word, + fullWord, mod, path: editor.getPath(), column: column + 1, @@ -59,7 +72,7 @@ class DatatipProvider { if (result.line) { const value = editor.lineTextForBufferRow(result.line).trim() return { - range, + range: result.local ? fullRange : range, markedStrings: [{ type: 'snippet', value, @@ -68,7 +81,7 @@ class DatatipProvider { } } else if (result.strings) { return { - range, + range: result.local ? fullRange : range, markedStrings: result.strings.map(string => { return { type: string.type, @@ -82,7 +95,7 @@ class DatatipProvider { if (result.line) { const value = editor.lineTextForBufferRow(result.line).trim() return { - range, + range: result.local ? fullRange : range, markedStrings: [{ type: 'snippet', value, @@ -92,7 +105,7 @@ class DatatipProvider { } else if (result.strings) { // @NOTE: atom-ide-datatip can't render multiple `snippet`s in `markedStrings` correctly return { - range, + range: result.local ? fullRange : range, markedStrings: result.strings.map(string => { return { type: 'markdown', diff --git a/lib/runtime/evaluation.coffee b/lib/runtime/evaluation.coffee index 6165b78c..b7310a85 100644 --- a/lib/runtime/evaluation.coffee +++ b/lib/runtime/evaluation.coffee @@ -85,8 +85,8 @@ module.exports = workspace.update() toggleDocs: (word, range) -> - {editor, mod, edpath} = @_currentContext() - {word, range} = words.getWord(editor) unless word? and range? + { editor, mod, edpath } = @_currentContext() + { word, range } = words.getWordAndRange(editor) unless word? and range? if word.length == 0 || !isNaN(word) then return searchDoc({word: word, mod: mod}).then (result) => if result.error then return diff --git a/lib/runtime/goto.js b/lib/runtime/goto.js index 728b5c9e..8629de0e 100644 --- a/lib/runtime/goto.js +++ b/lib/runtime/goto.js @@ -7,7 +7,12 @@ import { CompositeDisposable, Range } from 'atom' import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' -import { getWord, getWordRangeAtBufferPosition, isValidWordToInspect } from '../misc/words' +import { + wordRegexWithoutDotAccessor, + getWordAndRange, + getWordRangeAtBufferPosition, + isValidWordToInspect +} from '../misc/words' import { getLocalContext } from '../misc/blocks' const { @@ -34,11 +39,15 @@ class Goto { } getJumpFilePath(editor, bufferPosition) { - const includeRange = getWordRangeAtBufferPosition(editor, bufferPosition, includeRegex) + const includeRange = getWordRangeAtBufferPosition(editor, bufferPosition, { + wordRegex: includeRegex + }) if (includeRange.isEmpty()) return false // return if the bufferPosition is not on the path string - const filePathRange = getWordRangeAtBufferPosition(editor, bufferPosition, filePathRegex) + const filePathRange = getWordRangeAtBufferPosition(editor, bufferPosition, { + wordRegex: filePathRegex + }) if (filePathRange.isEmpty()) return false const filePathText = editor.getTextInBufferRange(filePathRange) @@ -71,8 +80,17 @@ class Goto { if (!this.isClientAndInkReady()) return - const { word } = getWord(editor, bufferPosition) - if (!isValidWordToInspect(word)) return + // get word w/ w/o dots at the buffer position + const { word } = getWordAndRange(editor, { + bufferPosition, + wordRegex: wordRegexWithoutDotAccessor + }) + const { word: fullWord } = getWordAndRange(editor, { + bufferPosition + }) + + // check the validity of code to be inspected + if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return // local context const { column, row } = bufferPosition @@ -85,6 +103,7 @@ class Goto { gotoSymbol({ word, + fullWord, path: editor.getPath(), // local context column: column + 1, @@ -123,12 +142,20 @@ class Goto { // If Julia is not running, do nothing if (!this.isClientAndInkReady()) return - const { word, range } = getWord(textEditor, bufferPosition) - // If the scope at `bufferPosition` is not valid code scope, do nothing if (!isValidScopeToInspect(textEditor, bufferPosition)) return - // Check the validity of code to be inspected - if (!isValidWordToInspect(word)) return + + // get word w/ w/o dots at the buffer position + const { word, range } = getWordAndRange(textEditor, { + bufferPosition, + wordRegex: wordRegexWithoutDotAccessor + }) + const { word: fullWord, range: fullRange } = getWordAndRange(textEditor, { + bufferPosition + }) + + // check the validity of code to be inspected + if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return // local context const { column, row } = bufferPosition @@ -139,9 +166,10 @@ class Goto { const mod = main ? (sub ? `${main}.${sub}` : main) : 'Main' const text = textEditor.getText() // buffer text that will be used for fallback entry - return new Promise((resolve, reject) => { + return new Promise((resolve) => { gotoSymbol({ word, + fullWord, path: textEditor.getPath(), // local context column: column + 1, @@ -161,7 +189,7 @@ class Goto { }) } resolve({ - range, + range: results.local ? fullRange : range, callback: () => { setTimeout(() => { this.ink.goto.goto(results, { From 530ba7e2e79e9e219433f365598eb05b57b2c2c2 Mon Sep 17 00:00:00 2001 From: aviatesk Date: Wed, 30 Oct 2019 00:59:00 +0900 Subject: [PATCH 2/5] fixed on workspace/docs --- lib/runtime/workspace.coffee | 1 + lib/ui/docs.js | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/runtime/workspace.coffee b/lib/runtime/workspace.coffee index 045d4a02..df409c82 100644 --- a/lib/runtime/workspace.coffee +++ b/lib/runtime/workspace.coffee @@ -41,6 +41,7 @@ module.exports = mod = if @mod == modules.follow then modules.current() else (@mod or 'Main') gotoSymbol word: name, + fullWord: name, mod: mod .then (symbols) => return if symbols.error diff --git a/lib/ui/docs.js b/lib/ui/docs.js index 20fda787..200484cd 100644 --- a/lib/ui/docs.js +++ b/lib/ui/docs.js @@ -69,6 +69,7 @@ export function processItem (item) { item.onClickName = () => { gotoSymbol({ word: item.name, + fullWord: item.name, mod: item.mod }).then(symbols => { if (symbols.error) return From 8cec8af8e0ba97b0dac1d525814fb19852217f17 Mon Sep 17 00:00:00 2001 From: aviatesk Date: Thu, 31 Oct 2019 03:52:29 +0900 Subject: [PATCH 3/5] strip trailing dots in frontend --- lib/misc/words.js | 78 ++++++++++++++++++++++++++--------- lib/runtime/datatip.js | 20 ++++----- lib/runtime/evaluation.coffee | 7 +++- lib/runtime/goto.js | 25 +++++------ lib/runtime/workspace.coffee | 1 - lib/ui/docs.js | 1 - 6 files changed, 82 insertions(+), 50 deletions(-) diff --git a/lib/misc/words.js b/lib/misc/words.js index 0b848ad2..fb704057 100644 --- a/lib/misc/words.js +++ b/lib/misc/words.js @@ -21,11 +21,15 @@ export function withWord (editor, fn) { * * `options` * - `bufferPosition` {Point}: If given returns the word at the `bufferPosition`, returns the word at the current cursor otherwise. - * - `wordRegex` {RegExp} : A RegExp indicating what constitutes a “word” (default: `wordRegex`). + * - `wordRegex` {RegExp} : A RegExp indicating what constitutes a “word”. Will be used if not both `beginWordRegex and `endWordRegex` are passed. (default: `wordRegex`). + * - `beginWordRegex` {RegExp} : A RegExp to find a beginning of a “word”. Should be passed with `endWordRegex`. (default: `undefined`). + * - `endWordRegex` {RegExp} : A RegExp to find an end of a “word”. Should be passed with `beginWordRegex`. (default: `undefined`). */ export function getWordAndRange (editor, options = { bufferPosition: undefined, - wordRegex: wordRegex + wordRegex: wordRegex, + beginWordRegex: undefined, + endWordRegex: undefined }) { // @TODO?: // The following lines are kinda iffy: The regex may or may not be well chosen @@ -33,35 +37,69 @@ export function getWordAndRange (editor, options = { // It might be better to select the current word via finding the smallest // containing the bufferPosition/cursor which also has `function` or `macro` as its class. const range = options.bufferPosition ? - getWordRangeAtBufferPosition(editor, options.bufferPosition, { - wordRegex: options.wordRegex ? options.wordRegex : wordRegex - }) : - editor.getLastCursor().getCurrentWordBufferRange({ - wordRegex: options.wordRegex ? options.wordRegex : wordRegex - }) + getWordRangeAtBufferPosition(editor, options.bufferPosition, options) : + getWordRangeAtBufferPosition(editor, editor.getLastCursor().getBufferPosition(), options) const word = editor.getTextInBufferRange(range) return { word, range } } /** * get the word under `bufferPosition` in `editor` - * adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616 * * `options` - * - `wordRegex` {RegExp}: A RegExp indicating what constitutes a “word” (default: `wordRegex`). + * - `wordRegex` {RegExp}: A RegExp indicating what constitutes a “word”. Will be used if not both `beginWordRegex and `endWordRegex` are passed. (default: `wordRegex`). + * - `beginWordRegex` {RegExp} : A RegExp to find a beginning of a “word”. Should be passed with `endWordRegex`. (default: `undefined`). + * - `endWordRegex` {RegExp} : A RegExp to find an end of a “word”. Should be passed with `beginWordRegex`. (default: `undefined`). */ export function getWordRangeAtBufferPosition(editor, bufferPosition, options = { - wordRegex: wordRegex + wordRegex: wordRegex, + beginWordRegex: undefined, + endWordRegex: undefined, }) { - const { row, column } = bufferPosition - const ranges = editor.getBuffer().findAllInRangeSync( - options.wordRegex ? options.wordRegex : wordRegex, - new Range(new Point(row, 0), new Point(row, Infinity)) - ) - const range = ranges.find(range => - range.end.column >= column && range.start.column <= column - ) - return range ? Range.fromObject(range) : new Range(bufferPosition, bufferPosition) + if (options.beginWordRegex && options.endWordRegex) { + // adapted from https://github.com/atom/atom/blob/1.42-releases/src/cursor.js#L572-L593 + const beginRange = new Range(new Point(bufferPosition.row, 0), bufferPosition) + const beginRanges = editor.getBuffer().findAllInRangeSync( + options.beginWordRegex, + beginRange + ) + let beginPosition + for (const range of beginRanges) { + if (bufferPosition.isLessThanOrEqual(range.start)) break + if (bufferPosition.isLessThanOrEqual(range.end)) { + beginPosition = Point.fromObject(range.start) + } + } + beginPosition = beginPosition || bufferPosition + + // https://github.com/atom/atom/blob/1.42-releases/src/cursor.js#L605-L624 + const endRange = new Range(bufferPosition, new Point(bufferPosition.row, Infinity)); + const endRanges = editor.getBuffer().findAllInRangeSync( + options.endWordRegex, + endRange + ) + let endPosition + for (const range of endRanges) { + if (bufferPosition.isLessThan(range.start)) break + if (bufferPosition.isLessThan(range.end)) { + endPosition = Point.fromObject(range.end) + } + } + endPosition = endPosition || bufferPosition + + return new Range(beginPosition, endPosition) + } else { + // adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616 + const { row, column } = bufferPosition + const ranges = editor.getBuffer().findAllInRangeSync( + options.wordRegex ? options.wordRegex : wordRegex, + new Range(new Point(row, 0), new Point(row, Infinity)) + ) + const range = ranges.find(range => + range.end.column >= column && range.start.column <= column + ) + return range ? Range.fromObject(range) : new Range(bufferPosition, bufferPosition) + } } /** diff --git a/lib/runtime/datatip.js b/lib/runtime/datatip.js index 60cda1f6..1f93bb30 100644 --- a/lib/runtime/datatip.js +++ b/lib/runtime/datatip.js @@ -12,6 +12,7 @@ import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' import { + wordRegex, wordRegexWithoutDotAccessor, getWordAndRange, isValidWordToInspect @@ -38,17 +39,15 @@ class DatatipProvider { // If the scope at `bufferPosition` is not valid code scope, do nothing if (!isValidScopeToInspect(editor, bufferPosition)) return - // get word w/ w/o dots at the buffer position + // get word without trailing dot accessors at the buffer position const { range, word } = getWordAndRange(editor, { bufferPosition, - wordRegex: wordRegexWithoutDotAccessor - }) - const { word: fullWord, range: fullRange } = getWordAndRange(editor, { - bufferPosition + beginWordRegex: wordRegex, + endWordRegex: wordRegexWithoutDotAccessor, }) // check the validity of code to be inspected - if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return + if (!(isValidWordToInspect(word))) return const { main, sub } = await modules.getEditorModule(editor, bufferPosition) const mod = main ? (sub ? `${main}.${sub}` : main) : 'Main' @@ -59,7 +58,6 @@ class DatatipProvider { try { const result = await datatip({ word, - fullWord, mod, path: editor.getPath(), column: column + 1, @@ -72,7 +70,7 @@ class DatatipProvider { if (result.line) { const value = editor.lineTextForBufferRow(result.line).trim() return { - range: result.local ? fullRange : range, + range, markedStrings: [{ type: 'snippet', value, @@ -81,7 +79,7 @@ class DatatipProvider { } } else if (result.strings) { return { - range: result.local ? fullRange : range, + range, markedStrings: result.strings.map(string => { return { type: string.type, @@ -95,7 +93,7 @@ class DatatipProvider { if (result.line) { const value = editor.lineTextForBufferRow(result.line).trim() return { - range: result.local ? fullRange : range, + range, markedStrings: [{ type: 'snippet', value, @@ -105,7 +103,7 @@ class DatatipProvider { } else if (result.strings) { // @NOTE: atom-ide-datatip can't render multiple `snippet`s in `markedStrings` correctly return { - range: result.local ? fullRange : range, + range, markedStrings: result.strings.map(string => { return { type: 'markdown', diff --git a/lib/runtime/evaluation.coffee b/lib/runtime/evaluation.coffee index b7310a85..67a71a71 100644 --- a/lib/runtime/evaluation.coffee +++ b/lib/runtime/evaluation.coffee @@ -86,8 +86,11 @@ module.exports = toggleDocs: (word, range) -> { editor, mod, edpath } = @_currentContext() - { word, range } = words.getWordAndRange(editor) unless word? and range? - if word.length == 0 || !isNaN(word) then return + { word, range } = words.getWordAndRange(editor, + beginWordRegex: words.wordRegex + endWordRegex: words.wordRegexWithoutDotAccessor + ) unless word? and range? + return unless words.isValidWordToInspect(word) searchDoc({word: word, mod: mod}).then (result) => if result.error then return v = views.render result diff --git a/lib/runtime/goto.js b/lib/runtime/goto.js index 8629de0e..34b2c74e 100644 --- a/lib/runtime/goto.js +++ b/lib/runtime/goto.js @@ -8,6 +8,7 @@ import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' import { + wordRegex, wordRegexWithoutDotAccessor, getWordAndRange, getWordRangeAtBufferPosition, @@ -80,17 +81,15 @@ class Goto { if (!this.isClientAndInkReady()) return - // get word w/ w/o dots at the buffer position + // get word without trailing dot accessors at the buffer position const { word } = getWordAndRange(editor, { bufferPosition, - wordRegex: wordRegexWithoutDotAccessor - }) - const { word: fullWord } = getWordAndRange(editor, { - bufferPosition + beginWordRegex: wordRegex, + endWordRegex: wordRegexWithoutDotAccessor, }) // check the validity of code to be inspected - if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return + if (!(isValidWordToInspect(word))) return // local context const { column, row } = bufferPosition @@ -103,7 +102,6 @@ class Goto { gotoSymbol({ word, - fullWord, path: editor.getPath(), // local context column: column + 1, @@ -145,17 +143,15 @@ class Goto { // If the scope at `bufferPosition` is not valid code scope, do nothing if (!isValidScopeToInspect(textEditor, bufferPosition)) return - // get word w/ w/o dots at the buffer position + // get word without trailing dot accessors at the buffer position const { word, range } = getWordAndRange(textEditor, { bufferPosition, - wordRegex: wordRegexWithoutDotAccessor - }) - const { word: fullWord, range: fullRange } = getWordAndRange(textEditor, { - bufferPosition + beginWordRegex: wordRegex, + endWordRegex: wordRegexWithoutDotAccessor, }) // check the validity of code to be inspected - if (!(isValidWordToInspect(word) && isValidWordToInspect(fullWord))) return + if (!(isValidWordToInspect(word))) return // local context const { column, row } = bufferPosition @@ -169,7 +165,6 @@ class Goto { return new Promise((resolve) => { gotoSymbol({ word, - fullWord, path: textEditor.getPath(), // local context column: column + 1, @@ -189,7 +184,7 @@ class Goto { }) } resolve({ - range: results.local ? fullRange : range, + range, callback: () => { setTimeout(() => { this.ink.goto.goto(results, { diff --git a/lib/runtime/workspace.coffee b/lib/runtime/workspace.coffee index df409c82..045d4a02 100644 --- a/lib/runtime/workspace.coffee +++ b/lib/runtime/workspace.coffee @@ -41,7 +41,6 @@ module.exports = mod = if @mod == modules.follow then modules.current() else (@mod or 'Main') gotoSymbol word: name, - fullWord: name, mod: mod .then (symbols) => return if symbols.error diff --git a/lib/ui/docs.js b/lib/ui/docs.js index 200484cd..20fda787 100644 --- a/lib/ui/docs.js +++ b/lib/ui/docs.js @@ -69,7 +69,6 @@ export function processItem (item) { item.onClickName = () => { gotoSymbol({ word: item.name, - fullWord: item.name, mod: item.mod }).then(symbols => { if (symbols.error) return From 66cfdd09dc2760d9fcc88146332c4ef290cc44fe Mon Sep 17 00:00:00 2001 From: aviatesk Date: Tue, 5 Nov 2019 17:08:10 +0900 Subject: [PATCH 4/5] `startRow` in `getLocalContext` now falls back to `undefined` --- lib/misc/blocks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/misc/blocks.js b/lib/misc/blocks.js index 7baa7774..4c281215 100644 --- a/lib/misc/blocks.js +++ b/lib/misc/blocks.js @@ -130,7 +130,7 @@ export function get (ed) { export function getLocalContext (editor, row) { const range = getRange(editor, row) const context = range ? editor.getTextInBufferRange(range) : '' - const startRow = range ? range[0][0] : null + const startRow = range ? range[0][0] : undefined return { context, startRow From d4358c0064e38f1725e4f30376bdb1dc43737aff Mon Sep 17 00:00:00 2001 From: aviatesk Date: Tue, 5 Nov 2019 18:04:30 +0900 Subject: [PATCH 5/5] simpler dot striping: - ref: https://github.com/JunoLab/atom-julia-client/pull/651#issuecomment-549259104 --- lib/misc/words.js | 109 +++++++++++++++------------------- lib/runtime/datatip.js | 11 ++-- lib/runtime/evaluation.coffee | 9 +-- lib/runtime/goto.js | 19 +++--- 4 files changed, 68 insertions(+), 80 deletions(-) diff --git a/lib/misc/words.js b/lib/misc/words.js index fb704057..35776ebe 100644 --- a/lib/misc/words.js +++ b/lib/misc/words.js @@ -3,7 +3,6 @@ import { Point, Range } from 'atom' export const wordRegex = /[\u00A0-\uFFFF\w_!´\.]*@?[\u00A0-\uFFFF\w_!´]+/ -export const wordRegexWithoutDotAccessor = /@?[\u00A0-\uFFFF\w_!´]+/ /** * Takes an `editor` and gets the word at current cursor position. If that is nonempty, call @@ -17,89 +16,79 @@ export function withWord (editor, fn) { } /** - * Gets the word and its range in the `editor` + * Returns the word and its range in the `editor`. * * `options` * - `bufferPosition` {Point}: If given returns the word at the `bufferPosition`, returns the word at the current cursor otherwise. - * - `wordRegex` {RegExp} : A RegExp indicating what constitutes a “word”. Will be used if not both `beginWordRegex and `endWordRegex` are passed. (default: `wordRegex`). - * - `beginWordRegex` {RegExp} : A RegExp to find a beginning of a “word”. Should be passed with `endWordRegex`. (default: `undefined`). - * - `endWordRegex` {RegExp} : A RegExp to find an end of a “word”. Should be passed with `beginWordRegex`. (default: `undefined`). + * - `wordRegex` {RegExp} : A RegExp indicating what constitutes a “word” (default: `wordRegex`). */ export function getWordAndRange (editor, options = { bufferPosition: undefined, - wordRegex: wordRegex, - beginWordRegex: undefined, - endWordRegex: undefined + wordRegex: wordRegex }) { // @TODO?: // The following lines are kinda iffy: The regex may or may not be well chosen // and it duplicates the efforts from atom-language-julia. // It might be better to select the current word via finding the smallest // containing the bufferPosition/cursor which also has `function` or `macro` as its class. - const range = options.bufferPosition ? - getWordRangeAtBufferPosition(editor, options.bufferPosition, options) : - getWordRangeAtBufferPosition(editor, editor.getLastCursor().getBufferPosition(), options) + const bufferPosition = options.bufferPosition ? + options.bufferPosition : + editor.getLastCursor().getBufferPosition() + const range = getWordRangeAtBufferPosition(editor, bufferPosition, { + wordRegex: options.wordRegex ? options.wordRegex : wordRegex + }) const word = editor.getTextInBufferRange(range) return { word, range } } /** - * get the word under `bufferPosition` in `editor` + * Returns the range of a word containing the `bufferPosition` in `editor`. * * `options` - * - `wordRegex` {RegExp}: A RegExp indicating what constitutes a “word”. Will be used if not both `beginWordRegex and `endWordRegex` are passed. (default: `wordRegex`). - * - `beginWordRegex` {RegExp} : A RegExp to find a beginning of a “word”. Should be passed with `endWordRegex`. (default: `undefined`). - * - `endWordRegex` {RegExp} : A RegExp to find an end of a “word”. Should be passed with `beginWordRegex`. (default: `undefined`). + * - `wordRegex` {RegExp}: A RegExp indicating what constitutes a “word” (default: `wordRegex`). */ -export function getWordRangeAtBufferPosition(editor, bufferPosition, options = { - wordRegex: wordRegex, - beginWordRegex: undefined, - endWordRegex: undefined, +export function getWordRangeAtBufferPosition (editor, bufferPosition, options = { + wordRegex: wordRegex }) { - if (options.beginWordRegex && options.endWordRegex) { - // adapted from https://github.com/atom/atom/blob/1.42-releases/src/cursor.js#L572-L593 - const beginRange = new Range(new Point(bufferPosition.row, 0), bufferPosition) - const beginRanges = editor.getBuffer().findAllInRangeSync( - options.beginWordRegex, - beginRange - ) - let beginPosition - for (const range of beginRanges) { - if (bufferPosition.isLessThanOrEqual(range.start)) break - if (bufferPosition.isLessThanOrEqual(range.end)) { - beginPosition = Point.fromObject(range.start) - } - } - beginPosition = beginPosition || bufferPosition + // adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616 + const { row, column } = bufferPosition + const ranges = editor.getBuffer().findAllInRangeSync( + options.wordRegex ? options.wordRegex : wordRegex, + new Range(new Point(row, 0), new Point(row, Infinity)) + ) + const range = ranges.find(range => + range.end.column >= column && range.start.column <= column + ) + return range ? Range.fromObject(range) : new Range(bufferPosition, bufferPosition) +} - // https://github.com/atom/atom/blob/1.42-releases/src/cursor.js#L605-L624 - const endRange = new Range(bufferPosition, new Point(bufferPosition.row, Infinity)); - const endRanges = editor.getBuffer().findAllInRangeSync( - options.endWordRegex, - endRange - ) - let endPosition - for (const range of endRanges) { - if (bufferPosition.isLessThan(range.start)) break - if (bufferPosition.isLessThan(range.end)) { - endPosition = Point.fromObject(range.end) - } - } - endPosition = endPosition || bufferPosition +/** + * Examples: `|` represents `bufferPosition`: + * - `"he|ad.word.foot"` => `Range` of `"head"` + * - `"head|.word.foot"` => `Range` of `"head"` + * - `"head.|word.foot"` => `Range` of `"head.word"` + * - `"head.word.fo|ot"` => `Range` of `"head.word.field"` + */ +export function getWordRangeWithoutTrailingDots (word, range, bufferPosition) { + const { start } = range + const { column: startColumn } = start + const { row: endRow } = range.end + let endColumn = startColumn + + const { column } = bufferPosition - return new Range(beginPosition, endPosition) - } else { - // adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616 - const { row, column } = bufferPosition - const ranges = editor.getBuffer().findAllInRangeSync( - options.wordRegex ? options.wordRegex : wordRegex, - new Range(new Point(row, 0), new Point(row, Infinity)) - ) - const range = ranges.find(range => - range.end.column >= column && range.start.column <= column - ) - return range ? Range.fromObject(range) : new Range(bufferPosition, bufferPosition) + const elements = word.split('.') + for (const element of elements) { + endColumn += element.length + if (column <= endColumn) { + break + } else { + endColumn += 1 + } } + + const end = new Point(endRow, endColumn) + return new Range(start, end) } /** diff --git a/lib/runtime/datatip.js b/lib/runtime/datatip.js index 1f93bb30..25c76ac5 100644 --- a/lib/runtime/datatip.js +++ b/lib/runtime/datatip.js @@ -12,9 +12,8 @@ import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' import { - wordRegex, - wordRegexWithoutDotAccessor, getWordAndRange, + getWordRangeWithoutTrailingDots, isValidWordToInspect } from '../misc/words' import { getLocalContext } from '../misc/blocks' @@ -40,11 +39,11 @@ class DatatipProvider { if (!isValidScopeToInspect(editor, bufferPosition)) return // get word without trailing dot accessors at the buffer position - const { range, word } = getWordAndRange(editor, { - bufferPosition, - beginWordRegex: wordRegex, - endWordRegex: wordRegexWithoutDotAccessor, + let { range, word } = getWordAndRange(editor, { + bufferPosition }) + range = getWordRangeWithoutTrailingDots(word, range, bufferPosition) + word = editor.getTextInBufferRange(range) // check the validity of code to be inspected if (!(isValidWordToInspect(word))) return diff --git a/lib/runtime/evaluation.coffee b/lib/runtime/evaluation.coffee index 67a71a71..7a3a194b 100644 --- a/lib/runtime/evaluation.coffee +++ b/lib/runtime/evaluation.coffee @@ -86,10 +86,11 @@ module.exports = toggleDocs: (word, range) -> { editor, mod, edpath } = @_currentContext() - { word, range } = words.getWordAndRange(editor, - beginWordRegex: words.wordRegex - endWordRegex: words.wordRegexWithoutDotAccessor - ) unless word? and range? + # get word without trailing dot accessors at the buffer position + { word, range } = words.getWordAndRange(editor) unless word? and range? + range = words.getWordRangeWithoutTrailingDots(word, range, bufferPosition) + word = editor.getTextInBufferRange(range) + return unless words.isValidWordToInspect(word) searchDoc({word: word, mod: mod}).then (result) => if result.error then return diff --git a/lib/runtime/goto.js b/lib/runtime/goto.js index 34b2c74e..776815c9 100644 --- a/lib/runtime/goto.js +++ b/lib/runtime/goto.js @@ -8,10 +8,9 @@ import { client } from '../connection' import modules from './modules' import { isValidScopeToInspect } from '../misc/scopes' import { - wordRegex, - wordRegexWithoutDotAccessor, getWordAndRange, getWordRangeAtBufferPosition, + getWordRangeWithoutTrailingDots, isValidWordToInspect } from '../misc/words' import { getLocalContext } from '../misc/blocks' @@ -82,11 +81,11 @@ class Goto { if (!this.isClientAndInkReady()) return // get word without trailing dot accessors at the buffer position - const { word } = getWordAndRange(editor, { - bufferPosition, - beginWordRegex: wordRegex, - endWordRegex: wordRegexWithoutDotAccessor, + let { word, range } = getWordAndRange(editor, { + bufferPosition }) + range = getWordRangeWithoutTrailingDots(word, range, bufferPosition) + word = editor.getTextInBufferRange(range) // check the validity of code to be inspected if (!(isValidWordToInspect(word))) return @@ -144,11 +143,11 @@ class Goto { if (!isValidScopeToInspect(textEditor, bufferPosition)) return // get word without trailing dot accessors at the buffer position - const { word, range } = getWordAndRange(textEditor, { - bufferPosition, - beginWordRegex: wordRegex, - endWordRegex: wordRegexWithoutDotAccessor, + let { word, range } = getWordAndRange(textEditor, { + bufferPosition }) + range = getWordRangeWithoutTrailingDots(word, range, bufferPosition) + word = textEditor.getTextInBufferRange(range) // check the validity of code to be inspected if (!(isValidWordToInspect(word))) return