mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
Merge pull request #32135 from overleaf/mg-comment-empty-select
Allow adding comments on empty selection GitOrigin-RevId: 14e742bb563fab99624be860691f1a9d2dabc00e
This commit is contained in:
+2
-1
@@ -16,6 +16,7 @@ import { isMac } from '@/shared/utils/os'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-properties-context'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
import { isCursorOnEmptyLine } from '@/features/source-editor/utils/is-cursor-on-empty-line'
|
||||
|
||||
const addCommentFromToolbar = () => commands.addComment('toolbar')
|
||||
|
||||
@@ -136,7 +137,7 @@ export const ToolbarItems: FC<{
|
||||
<ToolbarButton
|
||||
id="toolbar-add-comment"
|
||||
label={t('add_comment')}
|
||||
disabled={state.selection.main.empty}
|
||||
disabled={isCursorOnEmptyLine(state)}
|
||||
command={addCommentFromToolbar}
|
||||
icon="add_comment"
|
||||
/>
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
sendContextMenuEvent,
|
||||
ContextMenuItemSegmentation,
|
||||
} from '../utils/context-menu-analytics'
|
||||
import { isCursorOnEmptyLine } from '../utils/is-cursor-on-empty-line'
|
||||
|
||||
export const useContextMenuItems = () => {
|
||||
const view = useCodeMirrorViewContext()
|
||||
@@ -248,7 +249,7 @@ export const useContextMenuItems = () => {
|
||||
{
|
||||
label: t('comment'),
|
||||
handler: handleComment,
|
||||
disabled: !hasSelection,
|
||||
disabled: isCursorOnEmptyLine(state),
|
||||
show: permissions.comment,
|
||||
shortcut: getShortcut('insert-comment'),
|
||||
},
|
||||
|
||||
+4
-2
@@ -15,6 +15,7 @@ import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
||||
import { language } from '@codemirror/language'
|
||||
import { isCursorOnEmptyLine } from '@/features/source-editor/utils/is-cursor-on-empty-line'
|
||||
|
||||
export const useToolbarMenuBarEditorCommands = () => {
|
||||
const view = useCodeMirrorViewContext()
|
||||
@@ -25,6 +26,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
const { trackedWrite, comment } = usePermissionsContext()
|
||||
const languageName = state.facet(language)?.name
|
||||
const isTeXFile = languageName === 'latex'
|
||||
const canComment = !isCursorOnEmptyLine(state)
|
||||
|
||||
const openFigureModal = useCallback((source: FigureModalSource) => {
|
||||
window.dispatchEvent(
|
||||
@@ -174,7 +176,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
handler: () => {
|
||||
commands.addComment('toolbar')
|
||||
},
|
||||
disabled: !comment || state.selection.main.empty,
|
||||
disabled: !comment || !canComment,
|
||||
},
|
||||
/************************************
|
||||
* Format menu
|
||||
@@ -283,7 +285,7 @@ export const useToolbarMenuBarEditorCommands = () => {
|
||||
openFigureModal,
|
||||
trackedWrite,
|
||||
isTeXFile,
|
||||
state.selection.main.empty,
|
||||
canComment,
|
||||
comment,
|
||||
])
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { EditorState } from '@codemirror/state'
|
||||
|
||||
/**
|
||||
* Returns true when the cursor is on an empty line with no active selection.
|
||||
*
|
||||
* Used to disable the "Add comment" action — there is nothing to anchor a
|
||||
* comment to on a blank line.
|
||||
*/
|
||||
export function isCursorOnEmptyLine(state: EditorState): boolean {
|
||||
const { main } = state.selection
|
||||
|
||||
if (!main.empty) {
|
||||
return false
|
||||
}
|
||||
|
||||
const line = state.doc.lineAt(main.head)
|
||||
return line.text.trim().length === 0
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import { isCursorOnEmptyLine } from './is-cursor-on-empty-line'
|
||||
|
||||
/**
|
||||
* Returns the current highlighted range, or the nearest non-whitespace token
|
||||
* to the cursor when there is no active selection.
|
||||
*
|
||||
* "Token" here means any contiguous run of non-whitespace characters (`/\S+/`),
|
||||
* not necessarily an alphanumeric word. When two tokens are equidistant the
|
||||
* earlier one (closer to document start) wins.
|
||||
*
|
||||
* Only searches the current line.
|
||||
*
|
||||
* Only called on user-initiated actions (e.g. "Add comment"), not on every render.
|
||||
* Returns null when the cursor is on an empty/whitespace-only line.
|
||||
*/
|
||||
export function selectHighlightedOrNearestToken(
|
||||
state: EditorState
|
||||
): { from: number; to: number } | null {
|
||||
if (isCursorOnEmptyLine(state)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { main } = state.selection
|
||||
|
||||
if (!main.empty) {
|
||||
return { from: main.from, to: main.to }
|
||||
}
|
||||
|
||||
const pos = main.head
|
||||
const line = state.doc.lineAt(pos)
|
||||
|
||||
let bestFrom = 0
|
||||
let bestTo = 0
|
||||
let bestDist = Infinity
|
||||
|
||||
for (const match of line.text.matchAll(/\S+/g)) {
|
||||
const from = line.from + match.index!
|
||||
const to = from + match[0].length
|
||||
|
||||
let dist = 0
|
||||
if (pos < from) {
|
||||
dist = from - pos
|
||||
} else if (pos > to) {
|
||||
dist = pos - to
|
||||
}
|
||||
|
||||
if (dist === 0) {
|
||||
return { from, to }
|
||||
}
|
||||
|
||||
if (dist < bestDist) {
|
||||
bestFrom = from
|
||||
bestTo = to
|
||||
bestDist = dist
|
||||
}
|
||||
|
||||
// Past the cursor — no closer match possible on this line
|
||||
if (from > pos) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (bestDist < Infinity) {
|
||||
return { from: bestFrom, to: bestTo }
|
||||
}
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user