Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
28 changes: 28 additions & 0 deletions frontend/src/components/app-config/user-config-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,34 @@ export const UserConfigForm: React.FC = () => {
</div>
)}
/>
<FormField
control={form.control}
name="display.absolute_line_numbers"
render={({ field }) => (
<div className="flex flex-col space-y-1">
<FormItem className={formItemClasses}>
<FormLabel>Absolute line numbers</FormLabel>
<FormControl>
<Checkbox
data-testid="absolute-line-numbers-checkbox"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormMessage />
<IsOverridden
userConfig={config}
name="display.absolute_line_numbers"
/>
</FormItem>

<FormDescription>
Display line numbers relative to the Python script file
instead of relative to each cell.
</FormDescription>
</div>
)}
/>
</SettingGroup>
<SettingGroup title="Outputs">
<FormField
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/editor/cell/code/cell-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@

export interface CellEditorProps
extends Pick<CellRuntimeState, "status">,
Pick<CellData, "id" | "code" | "serializedEditorState" | "config"> {
Pick<
CellData,
"id" | "code" | "serializedEditorState" | "config" | "lineno"
> {
runCell: () => void;
theme: Theme;
showPlaceholder: boolean;
Expand Down Expand Up @@ -88,6 +91,7 @@
languageAdapter,
setLanguageAdapter,
showLanguageToggles = true,
lineno,
}: CellEditorProps) => {
const [aiCompletionCell, setAiCompletionCell] = useAtom(aiCompletionCellAtom);
const deleteCell = useDeleteCellCallback();
Expand Down Expand Up @@ -188,6 +192,7 @@
diagnosticsConfig: userConfig.diagnostics,
displayConfig: userConfig.display,
inlineAiTooltip: userConfig.ai?.inline_tooltip ?? false,
cellLineno: lineno,
});

extensions.push(
Expand Down Expand Up @@ -228,7 +233,7 @@
);

return extensions;
}, [

Check warning on line 236 in frontend/src/components/editor/cell/code/cell-editor.tsx

View workflow job for this annotation

GitHub Actions / 🧹 Lint frontend

React Hook useMemo has missing dependencies: 'lineno' and 'userConfig.ai?.inline_tooltip'. Either include them or remove the dependency array
cellId,
userConfig.keymap,
userConfig.completion,
Expand Down Expand Up @@ -381,7 +386,7 @@
]);

// Destroy the editor when the component is unmounted
useEffect(() => {

Check warning on line 389 in frontend/src/components/editor/cell/code/cell-editor.tsx

View workflow job for this annotation

GitHub Actions / 🧹 Lint frontend

This effect only uses props. Consider lifting the logic up to the parent
const ev = editorViewRef.current;
return () => {
ev?.destroy();
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/editor/notebook-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,7 @@ const EditableCellComponent = ({
showHiddenCode={showHiddenCode}
languageAdapter={languageAdapter}
setLanguageAdapter={setLanguageAdapter}
lineno={cellData.lineno}
/>
<CellRightSideActions
className={cn(
Expand Down Expand Up @@ -1099,6 +1100,7 @@ const SetupCellComponent = ({
languageAdapter={"python"}
setLanguageAdapter={Functions.NOOP}
showLanguageToggles={false}
lineno={cellData.lineno}
/>
<CellRightSideActions
edited={cellData.edited}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/core/cells/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function createCell({
edited = false,
config,
serializedEditorState = null,
lineno,
endLineno,
}: Partial<CellData> & { id: CellId }): CellData {
return {
id: id,
Expand All @@ -34,6 +36,8 @@ export function createCell({
lastCodeRun: lastCodeRun,
lastExecutionTime: lastExecutionTime,
serializedEditorState: serializedEditorState,
lineno: lineno,
endLineno: endLineno,
};
}

Expand Down Expand Up @@ -85,6 +89,10 @@ export interface CellData {
config: CellConfig;
/** serialized state of the underlying editor */
serializedEditorState: SerializedEditorState | null;
/** starting line number in the Python script file */
lineno?: number;
/** ending line number in the Python script file */
endLineno?: number;
}

export interface CellRuntimeState {
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/core/codemirror/absolute-line-numbers/extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* Copyright 2024 Marimo. All rights reserved. */

import type { Extension } from "@codemirror/state";
import { EditorView, lineNumbers } from "@codemirror/view";

/**
* Creates a CodeMirror extension that displays absolute line numbers
* based on the cell's position in the Python script file.
*
* @param lineOffset - The starting line number of the cell in the Python file (0-indexed)
* @returns CodeMirror extension for absolute line numbers
*/
export function absoluteLineNumbers(lineOffset: number): Extension {
return [
lineNumbers({
formatNumber: (lineNo: number) => {
// lineNo is 1-indexed within the cell
// lineOffset is the absolute starting line (0-indexed)
// We want to display: lineOffset + lineNo
return String(lineOffset + lineNo);
},
}),
// Add a visual indicator class to the editor
EditorView.editorAttributes.of({
class: "cm-absolute-line-numbers",
}),
];
}
17 changes: 15 additions & 2 deletions frontend/src/core/codemirror/cm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type {
} from "../config/config-schema";
import type { HotkeyProvider } from "../hotkeys/hotkeys";
import { store } from "../state/jotai";
import { absoluteLineNumbers } from "./absolute-line-numbers/extension";
import { requestEditCompletion } from "./ai/request";
import { cellBundle } from "./cells/extensions";
import type { CodemirrorCellActions } from "./cells/state";
Expand Down Expand Up @@ -81,8 +82,12 @@ export interface CodeMirrorSetupOpts {
hotkeys: HotkeyProvider;
lspConfig: LSPConfig;
diagnosticsConfig: DiagnosticsConfig;
displayConfig: Pick<DisplayConfig, "reference_highlighting">;
displayConfig: Pick<
DisplayConfig,
"reference_highlighting" | "absolute_line_numbers"
>;
inlineAiTooltip: boolean;
cellLineno?: number;
}

function getPlaceholderType(opts: CodeMirrorSetupOpts) {
Expand Down Expand Up @@ -180,9 +185,17 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
cellId,
lspConfig,
diagnosticsConfig,
displayConfig,
cellLineno,
} = opts;
const placeholderType = getPlaceholderType(opts);

// Use absolute line numbers if enabled and we have a valid line number
const useAbsoluteLineNumbers =
displayConfig.absolute_line_numbers &&
cellLineno !== undefined &&
cellLineno > 0;

return [
///// View
EditorView.lineWrapping,
Expand All @@ -191,7 +204,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
highlightActiveLine(),
highlightActiveLineGutter(),
highlightSpecialChars(),
lineNumbers(),
useAbsoluteLineNumbers ? absoluteLineNumbers(cellLineno) : lineNumbers(),
rectangularSelection(),
tooltips({
// Having fixed position prevents tooltips from being repositioned
Expand Down
1 change: 1 addition & 0 deletions frontend/src/core/config/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export const UserConfigSchema = z
}),
locale: z.string().nullable().optional(),
reference_highlighting: z.boolean().prefault(false),
absolute_line_numbers: z.boolean().prefault(false),
})
.prefault({}),
package_management: z
Expand Down
17 changes: 16 additions & 1 deletion frontend/src/core/edit-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
useCellActions,
} from "./cells/cells";
import { CellEffects } from "./cells/effects";
import { useResolvedMarimoConfig } from "./config/config";
import type { AppConfig, UserConfig } from "./config/config-schema";
import { RuntimeState } from "./kernel/RuntimeState";
import { getSessionId } from "./kernel/session";
Expand Down Expand Up @@ -66,7 +67,9 @@ export const EditApp: React.FC<AppProps> = ({
const hasCells = useAtomValue(hasCellsAtom);
const filename = useFilename();
const setLastSavedNotebook = useSetAtom(lastSavedNotebookAtom);
const { sendComponentValues, sendInterrupt } = useRequestClient();
const { sendComponentValues, sendInterrupt, saveUserConfig } =
useRequestClient();
const [config, setConfig] = useResolvedMarimoConfig();

const isEditing = viewState.mode === "edit";
const isPresenting = viewState.mode === "present";
Expand Down Expand Up @@ -132,6 +135,18 @@ export const EditApp: React.FC<AppProps> = ({
useHotkey("global.expandAllSections", () => {
expandAllCells();
});
useHotkey("global.toggleAbsoluteLineNumbers", () => {
const newConfig = {
...config,
display: {
...config.display,
absolute_line_numbers: !config.display.absolute_line_numbers,
},
};
saveUserConfig({ config: newConfig }).then(() => {
setConfig(newConfig);
});
});

const editableCellsArray = (
<CellArray
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/core/hotkeys/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ const DEFAULT_HOT_KEY = {
group: "Editing",
key: "Mod-Shift-\\",
},
"global.toggleAbsoluteLineNumbers": {
name: "Toggle absolute line numbers",
group: "Other",
key: "Mod-Shift-A",
},
"global.expandAllSections": {
name: "Expand all sections",
group: "Editing",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/core/kernel/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export function handleKernelReady(
last_execution_time = {},
app_config,
capabilities,
linenos = [],
end_linenos = [],
} = data;
const lastExecutedCode = last_executed_code || {};
const lastExecutionTime = last_execution_time || {};
Expand All @@ -78,6 +80,8 @@ export function handleKernelReady(
lastCodeRun: lastExecutedCode[cellId] ?? null,
lastExecutionTime: lastExecutionTime[cellId] ?? null,
config: configs[i],
lineno: linenos[i],
endLineno: end_linenos[i],
});
});

Expand Down
12 changes: 12 additions & 0 deletions frontend/src/css/app/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@
background-color: transparent;
}

/* Absolute line numbers indicator */
.cm-absolute-line-numbers::before {
content: "A";
position: absolute;
top: -2px;
left: 3px;
font-size: 10px;
font-weight: 600;
color: var(--gray-9);
z-index: 10;
}

.marimo-cell .cm-scroller {
/* this needs to be visible to show RTC cursors and inline AI edit buttons */
overflow: visible;
Expand Down
15 changes: 15 additions & 0 deletions marimo/_ast/cell_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ def register_cell(
config: Optional[CellConfig],
name: str = DEFAULT_CELL_NAME,
cell: Optional[Cell] = None,
lineno: int = 0,
end_lineno: int = 0,
) -> None:
"""Register a new cell with the manager.

Expand All @@ -208,6 +210,8 @@ def register_cell(
config: Cell configuration (column, disabled state, etc.)
name: Name of the cell, defaults to DEFAULT_CELL_NAME
cell: Optional Cell object for valid cells
lineno: Starting line number in the Python script file
end_lineno: Ending line number in the Python script file
"""
if cell_id is None:
cell_id = self.create_cell_id()
Expand All @@ -218,6 +222,8 @@ def register_cell(
name=name,
config=config or CellConfig(),
cell=cell,
lineno=lineno,
end_lineno=end_lineno,
)

def register_ir_cell(
Expand All @@ -244,10 +250,19 @@ def register_ir_cell(
config=cell_config,
name=cell_def.name,
cell=None,
lineno=cell_def.lineno,
end_lineno=cell_def.end_lineno,
)
return
cell._cell.configure(cell_config)
# Store line numbers in cell data after registering
self._register_cell(cell, app=app)
# Update with line numbers
if cell._cell.cell_id in self._cell_data:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this will work when you first load the notebook. But updating the notebook (adding/deleting cells, adding new lines etc etc) and this won't work anymore.

It's a nice idea, but there's more to keep the back end in sync with the frontend

self._cell_data[cell._cell.cell_id].lineno = cell_def.lineno
self._cell_data[
cell._cell.cell_id
].end_lineno = cell_def.end_lineno

def register_unparsable_cell(
self,
Expand Down
4 changes: 4 additions & 0 deletions marimo/_ast/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ class CellData:
name: User-provided name for the cell, or a default if none provided
config: Configuration options for the cell like column placement, disabled state, etc.
cell: The compiled Cell object if code is valid, None if code couldn't be parsed
lineno: Starting line number in the Python script file (0 if not from file)
end_lineno: Ending line number in the Python script file (0 if not from file)
"""

cell_id: CellId_t
code: str
name: str
config: CellConfig
cell: Optional[Cell]
lineno: int = 0
end_lineno: int = 0
2 changes: 2 additions & 0 deletions marimo/_config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class DisplayConfig(TypedDict):
- `default_table_page_size`: default number of rows to display in tables
- `default_table_max_columns`: default maximum number of columns to display in tables
- `reference_highlighting`: if `True`, highlight reactive variable references
- `absolute_line_numbers`: if `True`, display absolute line numbers from Python script
- `locale`: locale for date formatting and internationalization (e.g., "en-US", "en-GB", "de-DE")
"""

Expand All @@ -187,6 +188,7 @@ class DisplayConfig(TypedDict):
default_table_page_size: int
default_table_max_columns: int
reference_highlighting: NotRequired[bool]
absolute_line_numbers: NotRequired[bool]
locale: NotRequired[Optional[str]]


Expand Down
3 changes: 3 additions & 0 deletions marimo/_messaging/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@ class KernelReady(Op, tag="kernel-ready"):
kiosk: bool
# Kernel capabilities
capabilities: KernelCapabilities
# Absolute line numbers for cells in the Python script
linenos: tuple[int, ...] = ()
end_linenos: tuple[int, ...] = ()


class CompletionResult(Op, tag="completion-result"):
Expand Down
Loading
Loading