Merge branch 'development' into cherry-picking-onto-prs

This commit is contained in:
tidy-dev 2021-04-05 11:25:26 -04:00
commit 8844b91ad8
5 changed files with 170 additions and 27 deletions

View file

@ -94,6 +94,52 @@ export function lineNumberForDiffLine(
return -1
}
/**
* For the given row in the diff, determine the range of elements that
* should be displayed as interactive, as a hunk is not granular enough.
* The values in the returned range are mapped to lines in the original diff,
* in case the current diff has been partially expanded.
*/
export function findInteractiveOriginalDiffRange(
hunks: ReadonlyArray<DiffHunk>,
index: number
): IDiffRange | null {
const range = findInteractiveDiffRange(hunks, index)
if (range === null) {
return null
}
const from = getLineInOriginalDiff(hunks, range.from)
const to = getLineInOriginalDiff(hunks, range.to)
if (from === null || to === null) {
return null
}
return {
...range,
from,
to,
}
}
/**
* Utility function to get the line number in the original line from a given
* line number in the current text diff (which might be expanded).
*/
export function getLineInOriginalDiff(
hunks: ReadonlyArray<DiffHunk>,
index: number
) {
const diffLine = diffLineForIndex(hunks, index)
if (diffLine === null) {
return null
}
return diffLine.originalLineNumber
}
/**
* For the given row in the diff, determine the range of elements that
* should be displayed as interactive, as a hunk is not granular enough

View file

@ -28,7 +28,10 @@ import {
} from 'react-virtualized'
import { SideBySideDiffRow } from './side-by-side-diff-row'
import memoize from 'memoize-one'
import { findInteractiveDiffRange, DiffRangeType } from './diff-explorer'
import {
findInteractiveOriginalDiffRange,
DiffRangeType,
} from './diff-explorer'
import {
ChangedFile,
DiffRow,
@ -589,7 +592,7 @@ export class SideBySideDiff extends React.Component<
const selection = this.getSelection()
if (selection !== undefined) {
const range = findInteractiveDiffRange(diff.hunks, hunkStartLine)
const range = findInteractiveOriginalDiffRange(diff.hunks, hunkStartLine)
if (range !== null) {
const { from, to } = range
const sel = selection.withRangeSelection(from, to - from + 1, select)
@ -629,7 +632,7 @@ export class SideBySideDiff extends React.Component<
return
}
const range = findInteractiveDiffRange(diff.hunks, diffLineNumber)
const range = findInteractiveOriginalDiffRange(diff.hunks, diffLineNumber)
if (range === null || range.type === null) {
return
}
@ -656,7 +659,10 @@ export class SideBySideDiff extends React.Component<
return
}
const range = findInteractiveDiffRange(this.props.diff.hunks, hunkStartLine)
const range = findInteractiveOriginalDiffRange(
this.props.diff.hunks,
hunkStartLine
)
if (range === null || range.type === null) {
return
}

View file

@ -21,10 +21,11 @@ import { DiffSyntaxMode, IDiffSyntaxModeSpec } from './diff-syntax-mode'
import { CodeMirrorHost } from './code-mirror-host'
import {
diffLineForIndex,
findInteractiveDiffRange,
findInteractiveOriginalDiffRange,
lineNumberForDiffLine,
DiffRangeType,
diffLineInfoForIndex,
getLineInOriginalDiff,
} from './diff-explorer'
import {
@ -453,10 +454,15 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
this.cancelSelection()
}
const isSelected = !file.selection.isSelected(index)
const indexInOriginalDiff = getLineInOriginalDiff(hunks, index)
if (indexInOriginalDiff === null) {
return
}
const isSelected = !file.selection.isSelected(indexInOriginalDiff)
if (kind === 'hunk') {
const range = findInteractiveDiffRange(hunks, index)
const range = findInteractiveOriginalDiffRange(hunks, index)
if (!range) {
console.error('unable to find range for given line in diff')
return
@ -465,7 +471,12 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
const { from, to } = range
this.selection = { isSelected, from, to, kind }
} else if (kind === 'range') {
this.selection = { isSelected, from: index, to: index, kind }
this.selection = {
isSelected,
from: indexInOriginalDiff,
to: indexInOriginalDiff,
kind,
}
document.addEventListener('mousemove', this.onDocumentMouseMove)
} else {
assertNever(kind, `Unknown selection kind ${kind}`)
@ -496,9 +507,15 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
// pointer is placed underneath the last line so we clamp it
// to the range of valid values.
const max = Math.max(0, this.codeMirror.getDoc().lineCount() - 1)
const to = clamp(this.codeMirror.lineAtHeight(ev.y), 0, max)
const index = clamp(this.codeMirror.lineAtHeight(ev.y), 0, max)
this.codeMirror.scrollIntoView({ line: to, ch: 0 })
this.codeMirror.scrollIntoView({ line: index, ch: 0 })
const to = getLineInOriginalDiff(this.state.diff.hunks, index)
if (to === null) {
return
}
if (to !== this.selection.to) {
this.selection = { ...this.selection, to }
@ -526,11 +543,21 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
// we need to make sure the user is still within that hunk handle
// section and in the correct range.
if (this.selection.kind === 'hunk') {
const index = this.codeMirror.lineAtHeight(ev.y)
const indexInOriginalDiff = getLineInOriginalDiff(
this.state.diff.hunks,
index
)
if (indexInOriginalDiff === null) {
return
}
// Is the pointer over the same range (i.e hunk) that the
// selection was originally started from?
if (
indexInOriginalDiff === null ||
!targetHasClass(ev.target, 'hunk-handle') ||
!inSelection(this.selection, this.codeMirror.lineAtHeight(ev.y))
!inSelection(this.selection, indexInOriginalDiff)
) {
return this.cancelSelection()
}
@ -665,7 +692,10 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
return null
}
const range = findInteractiveDiffRange(this.state.diff.hunks, lineNumber)
const range = findInteractiveOriginalDiffRange(
this.state.diff.hunks,
lineNumber
)
if (range === null) {
return null
}
@ -1189,20 +1219,21 @@ export class TextDiff extends React.Component<ITextDiffProps, ITextDiffState> {
return
}
const lineNumber = this.codeMirror.lineAtHeight(ev.y)
const diffLine = diffLineForIndex(this.state.diff.hunks, lineNumber)
const hunks = this.state.diff.hunks
const diffLine = diffLineForIndex(hunks, lineNumber)
if (!diffLine || !diffLine.isIncludeableLine()) {
return
}
const range = findInteractiveDiffRange(this.state.diff.hunks, lineNumber)
const range = findInteractiveOriginalDiffRange(hunks, lineNumber)
if (range === null) {
return
}
const { from, to } = range
this.hunkHighlightRange = { from, to, kind: 'hunk', isSelected: false }
this.updateViewport()
}

View file

@ -33,7 +33,8 @@ interface IDraggableProps {
}
export class Draggable extends React.Component<IDraggableProps> {
private dragStarted: boolean = false
private hasDragStarted: boolean = false
private hasDragEnded: boolean = false
private dragElement: HTMLElement | null = null
private elemBelow: Element | null = null
// Default offset to place the cursor slightly above the top left corner of
@ -41,7 +42,6 @@ export class Draggable extends React.Component<IDraggableProps> {
// dragElement then elemBelow will always return the dragElement and cannot
// detect drop targets or scroll elements.
private verticalOffset: number = __DARWIN__ ? 32 : 15
private hasMouseUpOccurred = false
public componentDidMount() {
this.dragElement = document.getElementById('dragElement')
@ -58,7 +58,7 @@ export class Draggable extends React.Component<IDraggableProps> {
}
private initializeDrag(): void {
this.dragStarted = false
this.hasDragStarted = false
this.elemBelow = null
}
@ -72,10 +72,10 @@ export class Draggable extends React.Component<IDraggableProps> {
if (!this.canDragCommit(event)) {
return
}
this.hasMouseUpOccurred = false
document.onmouseup = this.onMouseUp
this.hasDragEnded = false
document.onmouseup = this.handleDragEndEvent
await sleep(100)
if (this.hasMouseUpOccurred) {
if (this.hasDragEnded) {
return
}
@ -91,16 +91,17 @@ export class Draggable extends React.Component<IDraggableProps> {
* just clicking a draggable element.
*/
private onMouseMove = (moveEvent: MouseEvent) => {
if (this.hasMouseUpOccurred) {
if (this.hasDragEnded) {
this.onDragEnd()
return
}
// start drag
if (!this.dragStarted) {
if (!this.hasDragStarted) {
this.props.onRenderDragElement()
this.props.onDragStart()
dragAndDropManager.dragStarted()
this.dragStarted = true
this.hasDragStarted = true
window.addEventListener('keyup', this.onKeyUp)
}
// move drag element where mouse is
@ -126,12 +127,20 @@ export class Draggable extends React.Component<IDraggableProps> {
/**
* End a drag event
*/
private onMouseUp = () => {
this.hasMouseUpOccurred = true
if (this.dragStarted) {
private handleDragEndEvent = () => {
this.hasDragEnded = true
if (this.hasDragStarted) {
this.onDragEnd()
}
document.onmouseup = null
window.removeEventListener('keyup', this.onKeyUp)
}
private onKeyUp = (event: KeyboardEvent) => {
if (event.key !== 'Escape') {
return
}
this.handleDragEndEvent()
}
private onDragEnd(): void {

View file

@ -118,9 +118,60 @@ function mergeDeferredContextMenuItems(
})
}
if (!__DARWIN__) {
// NOTE: "On macOS as we use the native APIs there is no way to set the
// language that the spellchecker uses" -- electron docs Therefore, we are
// only allowing setting to English for non-mac machines.
const spellCheckLanguageItem = getSpellCheckLanguageMenuItem(
webContents.session
)
if (spellCheckLanguageItem !== null) {
items.push(spellCheckLanguageItem)
}
}
showContextualMenu(items, false)
}
/**
* Method to get a menu item to give user the option to use English or their
* system language.
*
* If system language is english, it returns null. If spellchecker is not set to
* english, it returns item that can set it to English. If spellchecker is set
* to english, it returns the item that can set it to their system language.
*/
function getSpellCheckLanguageMenuItem(
session: Electron.session
): IMenuItem | null {
const userLanguageCode = remote.app.getLocale()
const englishLanguageCode = 'en-US'
const spellcheckLanguageCodes = session.getSpellCheckerLanguages()
if (
userLanguageCode === englishLanguageCode &&
spellcheckLanguageCodes.includes(englishLanguageCode)
) {
return null
}
const languageCode =
spellcheckLanguageCodes.includes(englishLanguageCode) &&
!spellcheckLanguageCodes.includes(userLanguageCode)
? userLanguageCode
: englishLanguageCode
const label =
languageCode === englishLanguageCode
? 'Set spellcheck to English'
: 'Set spellcheck to system language'
return {
label,
action: () => session.setSpellCheckerLanguages([languageCode]),
}
}
/** Show the given menu items in a contextual menu. */
export async function showContextualMenu(
items: ReadonlyArray<IMenuItem>,