alm
Version:
The best IDE for TypeScript
173 lines (151 loc) • 5.18 kB
text/typescript
/**
* Some useful monaco utilities
*/
/** Some types */
type Editor = monaco.editor.ICommonCodeEditor;
let editorOperationCounter = 0;
export function replaceSelection(config: {
editor: Editor,
newText: string
}) {
const selection = config.editor.getSelection();
const editOperation: monaco.editor.IIdentifiedSingleEditOperation = {
identifier: {
major: 0,
minor: ++editorOperationCounter,
},
text: config.newText,
range: selection,
forceMoveMarkers: false,
}
config.editor.getModel().pushEditOperations([], [editOperation], null);
}
export function replaceRange(config: {
model: monaco.IModel,
range: {
startLineNumber: number,
startColumn: number,
endLineNumber: number,
endColumn: number
},
newText: string
}) {
const editOperation: monaco.editor.IIdentifiedSingleEditOperation = {
identifier: {
major: 0,
minor: ++editorOperationCounter,
},
text: config.newText,
range: new monaco.Range(config.range.startLineNumber, config.range.startColumn, config.range.endLineNumber, config.range.endColumn),
forceMoveMarkers: false,
isAutoWhitespaceEdit: false,
}
config.model.pushEditOperations([], [editOperation], null);
}
export function writeString(config: {
model: monaco.IModel,
str: string,
pos: {
lineNumber: number,
column: number
}
}) {
const editOperation: monaco.editor.IIdentifiedSingleEditOperation = {
identifier: {
major: 0,
minor: ++editorOperationCounter,
},
text: config.str,
range: new monaco.Range(config.pos.lineNumber, config.pos.column, config.pos.lineNumber, config.pos.column),
/** Needed to change selection */
forceMoveMarkers: true,
}
config.model.pushEditOperations([], [editOperation], null);
}
/** Runs format or format selection (if any) */
export function format(config: {
editor: Editor,
}) {
const action = config.editor.getAction('editor.action.format');
action.run();
}
/**
* Useful for language query stuff that we want to debounce + cancel if no longer relevant even after the query is made
*/
export function onlyLastCallWithDelay<T>(call: () => Promise<T>, token: monaco.CancellationToken): Promise<T> {
let delay = 500;
let timeout: any;
const p = new Promise<T>((resolve, reject) => {
const later = () => {
if (token.isCancellationRequested) reject('cancelled');
else {
call().then((res) => {
if (token.isCancellationRequested) reject('cancelled');
else resolve(res);
});
}
}
timeout = setTimeout(later, delay);
token.onCancellationRequested(() => {
clearTimeout(timeout);
reject('cancelled');
});
})
return p;
}
export function setSelection(cfg: { editor: Editor, textSpan: ts.TextSpan }) {
const model = cfg.editor.getModel();
let start = model.getPositionAt(cfg.textSpan.start);
let end = model.getPositionAt(cfg.textSpan.start + cfg.textSpan.length);
cfg.editor.setSelection({
startLineNumber: start.lineNumber,
startColumn: start.column,
endLineNumber: end.lineNumber,
endColumn: end.column
});
}
export function gotoPosition(cfg: { editor: Editor, position: EditorPosition }) {
const pos = {
lineNumber: cfg.position.line + 1,
column: cfg.position.ch + 1,
};
cfg.editor.setPosition(pos);
cfg.editor.revealPosition(pos);
}
export function getVisibleLines(editor: Editor): monaco.Range {
// HACK: The current lines visible api
const range: monaco.Range = (editor as any)._view.layoutProvider.getLinesViewportData().visibleRange;
return range;
}
/** Note: Only useful if in single cursor mode */
export function isCursorInTopHalf(cm: Editor): boolean {
let cursor = cm.getPosition();
let scrollInfo = getVisibleLines(cm);
// Closer to top than bottom
return (cursor.lineNumber - scrollInfo.startLineNumber) < (scrollInfo.endLineNumber - cursor.lineNumber);
}
export function getSelectionOrCurrentLine(editor: Editor): string {
const selection = editor.getSelection();
let hasSelection = !selection.isEmpty();
if (hasSelection) {
let selected = editor.getModel().getValueInRange(selection);
return selected;
}
else {
let selected = editor.getModel().getLineContent(selection.startLineNumber);
return selected;
}
}
/**
* Position conversion functions
*/
export function getCurrentPosition(editor: Editor): number {
const position = editor.getPosition();
return editor.getModel().getOffsetAt(position);
}
export function positionToOffset(model: monaco.editor.IReadOnlyModel, position: monaco.IPosition): number {
return model.getOffsetAt(position);
}
export function offsetToPosition(model: monaco.editor.IReadOnlyModel, offset: number): monaco.IPosition {
return model.getPositionAt(offset);
}