Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: strip trailing dots #651

Merged
merged 5 commits into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/misc/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 52 additions & 12 deletions lib/misc/words.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,51 @@ export const wordRegex = /[\u00A0-\uFFFF\w_!´\.]*@?[\u00A0-\uFFFF\w_!´]+/
* 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)
}

/**
* Gets the word and its range in the `editor`
* Returns 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 <span>
// containing the bufferPosition/cursor which also has `function` or `macro` as its class.
const range = bufferPosition ?
getWordRangeAtBufferPosition(editor, bufferPosition) :
editor.getLastCursor().getCurrentWordBufferRange({ wordRegex })
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`
* adapted from https://github.com/atom/atom/blob/v1.38.2/src/cursor.js#L606-L616
* Returns the range of a word containing the `bufferPosition` in `editor`.
*
* - 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
}) {
// 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(
optionalWordRegex || wordRegex,
options.wordRegex ? options.wordRegex : wordRegex,
new Range(new Point(row, 0), new Point(row, Infinity))
)
const range = ranges.find(range =>
Expand All @@ -51,6 +62,35 @@ export function getWordRangeAtBufferPosition(editor, bufferPosition, optionalWor
return range ? Range.fromObject(range) : new Range(bufferPosition, 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

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)
}

/**
* Returns `true` if `word` is valid word to be inspected.
*/
Expand Down
18 changes: 14 additions & 4 deletions lib/runtime/datatip.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@
import { client } from '../connection'
import modules from './modules'
import { isValidScopeToInspect } from '../misc/scopes'
import { getWord, isValidWordToInspect } from '../misc/words'
import {
getWordAndRange,
getWordRangeWithoutTrailingDots,
isValidWordToInspect
} from '../misc/words'
import { getLocalContext } from '../misc/blocks'

const datatip = client.import('datatip')
Expand All @@ -34,9 +38,15 @@ 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 without trailing dot accessors at the buffer position
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

const { main, sub } = await modules.getEditorModule(editor, bufferPosition)
const mod = main ? (sub ? `${main}.${sub}` : main) : 'Main'
Expand Down
10 changes: 7 additions & 3 deletions lib/runtime/evaluation.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,13 @@ module.exports =
workspace.update()

toggleDocs: (word, range) ->
{editor, mod, edpath} = @_currentContext()
{word, range} = words.getWord(editor) unless word? and range?
if word.length == 0 || !isNaN(word) then return
{ editor, mod, edpath } = @_currentContext()
# 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
v = views.render result
Expand Down
42 changes: 32 additions & 10 deletions lib/runtime/goto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
getWordAndRange,
getWordRangeAtBufferPosition,
getWordRangeWithoutTrailingDots,
isValidWordToInspect
} from '../misc/words'
import { getLocalContext } from '../misc/blocks'

const {
Expand All @@ -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)
Expand Down Expand Up @@ -71,8 +80,15 @@ class Goto {

if (!this.isClientAndInkReady()) return

const { word } = getWord(editor, bufferPosition)
if (!isValidWordToInspect(word)) return
// get word without trailing dot accessors at the buffer position
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

// local context
const { column, row } = bufferPosition
Expand Down Expand Up @@ -123,12 +139,18 @@ 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 without trailing dot accessors at the buffer position
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

// local context
const { column, row } = bufferPosition
Expand All @@ -139,7 +161,7 @@ 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,
path: textEditor.getPath(),
Expand Down