Skip to content

Commit 87a7451

Browse files
authored
Merge pull request #1136 from rust-lang/editor-goto-line
Enhance go-to-position for the simple and Monaco editors
2 parents b5353e6 + 8d359da commit 87a7451

File tree

2 files changed

+112
-85
lines changed

2 files changed

+112
-85
lines changed

ui/frontend/editor/MonacoEditorCore.tsx

Lines changed: 107 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const MonacoEditorCore: React.FC<CommonEditorProps> = (props) => {
7373
fontFamily: nodeStyle.fontFamily,
7474
automaticLayout: true,
7575
'semanticHighlighting.enabled': true,
76+
autoClosingOvertype: 'always',
7677
});
7778
setEditor(editor);
7879

@@ -81,93 +82,122 @@ const MonacoEditorCore: React.FC<CommonEditorProps> = (props) => {
8182
editor.focus();
8283
}, []);
8384

84-
useEditorProp(editor, props.onEditCode, (_editor, model, onEditCode) => {
85-
model.onDidChangeContent(() => {
86-
onEditCode(model.getValue());
87-
});
88-
});
89-
90-
useEditorProp(editor, props.execute, (editor, _model, execute) => {
91-
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
92-
execute();
93-
});
94-
// Ace's Vim mode runs code with :w, so let's do the same
95-
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
96-
execute();
97-
});
98-
});
85+
useEditorProp(
86+
editor,
87+
props.onEditCode,
88+
useCallback((_editor, model, onEditCode) => {
89+
model.onDidChangeContent(() => {
90+
onEditCode(model.getValue());
91+
});
92+
}, []),
93+
);
9994

100-
useEditorProp(editor, props.code, (editor, model, code) => {
101-
// Short-circuit if nothing interesting to change.
102-
if (code === model.getValue()) {
103-
return;
104-
}
95+
useEditorProp(
96+
editor,
97+
props.execute,
98+
useCallback((editor, _model, execute) => {
99+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
100+
execute();
101+
});
102+
// Ace's Vim mode runs code with :w, so let's do the same
103+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
104+
execute();
105+
});
106+
}, []),
107+
);
105108

106-
editor.executeEdits('redux', [
107-
{
108-
text: code,
109-
range: model.getFullModelRange(),
110-
},
111-
]);
112-
});
109+
useEditorProp(
110+
editor,
111+
props.code,
112+
useCallback((editor, model, code) => {
113+
// Short-circuit if nothing interesting to change.
114+
if (code === model.getValue()) {
115+
return;
116+
}
117+
118+
editor.executeEdits('redux', [
119+
{
120+
text: code,
121+
range: model.getFullModelRange(),
122+
},
123+
]);
124+
}, []),
125+
);
113126

114-
useEditorProp(editor, theme, (editor, _model, theme) => {
115-
editor.updateOptions({ theme });
116-
});
127+
useEditorProp(
128+
editor,
129+
theme,
130+
useCallback((editor, _model, theme) => {
131+
editor.updateOptions({ theme });
132+
}, []),
133+
);
117134

118135
const autocompleteProps = useMemo(
119136
() => ({ autocompleteOnUse, crates: props.crates }),
120137
[autocompleteOnUse, props.crates],
121138
);
122139

123-
useEditorProp(editor, autocompleteProps, (_editor, _model, { autocompleteOnUse, crates }) => {
124-
completionProvider.current = monaco.languages.registerCompletionItemProvider('rust', {
125-
triggerCharacters: [' '],
126-
127-
provideCompletionItems(model, position, _context, _token) {
128-
const word = model.getWordUntilPosition(position);
129-
130-
function wordBefore(
131-
word: monaco.editor.IWordAtPosition,
132-
): monaco.editor.IWordAtPosition | null {
133-
const prevPos = { lineNumber: position.lineNumber, column: word.startColumn - 1 };
134-
return model.getWordAtPosition(prevPos);
135-
}
136-
137-
const preWord = wordBefore(word);
138-
const prePreWord = preWord && wordBefore(preWord);
139-
140-
const oldStyle = prePreWord?.word === 'extern' && preWord?.word === 'crate';
141-
const newStyle = autocompleteOnUse && preWord?.word === 'use';
142-
143-
const triggerPrefix = oldStyle || newStyle;
144-
145-
if (!triggerPrefix) {
146-
return { suggestions: [] };
147-
}
148-
149-
const range = {
150-
startLineNumber: position.lineNumber,
151-
endLineNumber: position.lineNumber,
152-
startColumn: word.startColumn,
153-
endColumn: word.endColumn,
154-
};
155-
156-
const suggestions = crates.map(({ name, version, id }) => ({
157-
kind: monaco.languages.CompletionItemKind.Module,
158-
label: `${name} (${version})`,
159-
insertText: `${id}; // ${version}`,
160-
range,
161-
}));
162-
163-
return { suggestions };
164-
},
165-
});
140+
useEditorProp(
141+
editor,
142+
autocompleteProps,
143+
useCallback((_editor, _model, { autocompleteOnUse, crates }) => {
144+
completionProvider.current = monaco.languages.registerCompletionItemProvider('rust', {
145+
triggerCharacters: [' '],
146+
147+
provideCompletionItems(model, position, _context, _token) {
148+
const word = model.getWordUntilPosition(position);
149+
150+
function wordBefore(
151+
word: monaco.editor.IWordAtPosition,
152+
): monaco.editor.IWordAtPosition | null {
153+
const prevPos = { lineNumber: position.lineNumber, column: word.startColumn - 1 };
154+
return model.getWordAtPosition(prevPos);
155+
}
156+
157+
const preWord = wordBefore(word);
158+
const prePreWord = preWord && wordBefore(preWord);
159+
160+
const oldStyle = prePreWord?.word === 'extern' && preWord?.word === 'crate';
161+
const newStyle = autocompleteOnUse && preWord?.word === 'use';
162+
163+
const triggerPrefix = oldStyle || newStyle;
164+
165+
if (!triggerPrefix) {
166+
return { suggestions: [] };
167+
}
168+
169+
const range = {
170+
startLineNumber: position.lineNumber,
171+
endLineNumber: position.lineNumber,
172+
startColumn: word.startColumn,
173+
endColumn: word.endColumn,
174+
};
175+
176+
const suggestions = crates.map(({ name, version, id }) => ({
177+
kind: monaco.languages.CompletionItemKind.Module,
178+
label: `${name} (${version})`,
179+
insertText: `${id}; // ${version}`,
180+
range,
181+
}));
182+
183+
return { suggestions };
184+
},
185+
});
186+
187+
return () => {
188+
completionProvider.current?.dispose();
189+
};
190+
}, []),
191+
);
166192

167-
return () => {
168-
completionProvider.current?.dispose();
169-
};
170-
});
193+
useEditorProp(
194+
editor,
195+
props.position,
196+
useCallback((editor, _model, { line, column }) => {
197+
editor.setPosition({ lineNumber: line, column });
198+
editor.focus();
199+
}, []),
200+
);
171201

172202
return <div className={styles.monaco} ref={child} />;
173203
};

ui/frontend/editor/SimpleEditor.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { isEqual } from 'lodash-es';
21
import React from 'react';
32

43
import { CommonEditorProps, Position, Selection } from '../types';
@@ -16,11 +15,9 @@ class CodeByteOffsets {
1615

1716
public lineToOffsets(line: number) {
1817
const precedingBytes = this.bytesBeforeLine(line);
18+
const nextPrecedingBytes = this.bytesBeforeLine(line + 1);
1919

20-
const highlightedLine = this.lines[line];
21-
const highlightedBytes = highlightedLine.length;
22-
23-
return [precedingBytes, precedingBytes + highlightedBytes];
20+
return [precedingBytes, nextPrecedingBytes];
2421
}
2522

2623
public rangeToOffsets(start: Position, end: Position) {
@@ -41,7 +38,7 @@ class CodeByteOffsets {
4138
const precedingLines = this.lines.slice(0, line);
4239

4340
// Add one to account for the newline we split on and removed
44-
return precedingLines.map((l) => l.length + 1).reduce((a, b) => a + b);
41+
return precedingLines.map((l) => l.length + 1).reduce((a, b) => a + b, 0);
4542
}
4643
}
4744

@@ -87,7 +84,7 @@ class SimpleEditor extends React.PureComponent<CommonEditorProps> {
8784
if (!newPosition || !editor) {
8885
return;
8986
}
90-
if (isEqual(newPosition, oldPosition)) {
87+
if (newPosition === oldPosition) {
9188
return;
9289
}
9390

@@ -104,7 +101,7 @@ class SimpleEditor extends React.PureComponent<CommonEditorProps> {
104101
if (!newSelection || !newSelection.start || !newSelection.end || !editor) {
105102
return;
106103
}
107-
if (isEqual(newSelection, oldSelection)) {
104+
if (newSelection === oldSelection) {
108105
return;
109106
}
110107

0 commit comments

Comments
 (0)