Skip to content

Commit 5586db5

Browse files
committed
Guess at indentation, to avoid UI flicker.
`window.showTextDocument()` allows access to more accurate indentation settings, but it creates jarring flashes in the UI, and for the corner cases where it makes a difference, I don't think it's worth it.
1 parent a1bd8c5 commit 5586db5

File tree

1 file changed

+101
-35
lines changed

1 file changed

+101
-35
lines changed

src/cmakeListsModifier.ts

Lines changed: 101 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,11 @@ export class CMakeListsModifier implements vscode.Disposable {
293293

294294
const cmakeDocument = sourceList.document;
295295
const insertPos = sourceList.insertPosition;
296-
const prefix = await indentPrefix(sourceList.invocation, insertPos);
296+
const indent = freshLineIndent(sourceList.invocation, insertPos);
297297
const newSourceArgument = quoteArgument(sourceList.relativePath(newSourceUri));
298298
const edit = new vscode.WorkspaceEdit();
299299
edit.insert(
300-
cmakeDocument.uri, insertPos, `${prefix}${newSourceArgument}`,
300+
cmakeDocument.uri, insertPos, `\n${indent}${newSourceArgument}`,
301301
{
302302
label: 'CMake: Add new source file',
303303
needsConfirmation: settings.addNewSourceFiles === 'ask'
@@ -1298,41 +1298,107 @@ async function quickPick<T>(
12981298
return selected.payload;
12991299
}
13001300

1301-
async function indentPrefix(invocation: CommandInvocation, insertPos: vscode.Position) {
1302-
const activeEditor = vscode.window.activeTextEditor;
1303-
// Wish this didn't flash the CMake file in the UI, but the API doesn't
1304-
// expose another way to get the inferred indentation.
1305-
const editor = await vscode.window.showTextDocument(
1306-
invocation.document, {
1307-
preview: true,
1308-
preserveFocus: true
1301+
function freshLineIndent(invocation: CommandInvocation, insertPos: vscode.Position) {
1302+
const currentLine = invocation.document.lineAt(insertPos.line);
1303+
const currentLineIndent =
1304+
currentLine.text.slice(0, currentLine.firstNonWhitespaceCharacterIndex);
1305+
1306+
if (invocation.line !== insertPos.line) {
1307+
// Just keep the current indentation
1308+
return currentLineIndent;
1309+
}
1310+
1311+
const guessed = guessIndentConfig(invocation.document);
1312+
const currentLineIndentSize = Array.from(currentLineIndent)
1313+
.reduce((n, c) => n + (c === '\t' ? guessed.tabSize : 1), 0);
1314+
const freshLineIndentSize = currentLineIndentSize + guessed.indentSize;
1315+
1316+
if (guessed.insertSpaces) {
1317+
return ' '.repeat(freshLineIndentSize);
1318+
}
1319+
1320+
const tabs = Math.floor(freshLineIndentSize / guessed.tabSize);
1321+
const spaces = freshLineIndentSize % guessed.tabSize;
1322+
return '\t'.repeat(tabs) + ' '.repeat(spaces);
1323+
}
1324+
1325+
interface IndentConfig {
1326+
tabSize: number;
1327+
indentSize: number;
1328+
insertSpaces: boolean;
1329+
}
1330+
1331+
function guessIndentConfig(document: vscode.TextDocument): IndentConfig {
1332+
const { tabSize, indentSize, insertSpaces } = indentSettings(document);
1333+
1334+
let tabs = false;
1335+
let minSpaces = 0; let maxSpaces = 0;
1336+
for (const line of documentLines(document)) {
1337+
const indent = line.text.slice(0, line.firstNonWhitespaceCharacterIndex);
1338+
if (indent.startsWith('\t')) {
1339+
tabs = true;
1340+
} else if (indent.startsWith(' ')) {
1341+
const matches = indent.match('^( *)') as RegExpMatchArray;
1342+
const spacesSize = matches[1].length;
1343+
if (!minSpaces || spacesSize < minSpaces) {
1344+
minSpaces = spacesSize;
1345+
}
1346+
if (spacesSize > maxSpaces) {
1347+
maxSpaces = spacesSize;
1348+
}
13091349
}
1310-
);
1311-
const tabSize = editor.options.tabSize as number;
1312-
const indentSize = editor.options.indentSize as number;
1313-
const insertSpaces = editor.options.insertSpaces;
1314-
if (activeEditor) {
1315-
await vscode.window.showTextDocument(activeEditor.document);
1316-
}
1317-
const thisLine = invocation.document.lineAt(insertPos.line);
1318-
const thisLineIndent = thisLine.text.slice(0, thisLine.firstNonWhitespaceCharacterIndex);
1319-
const thisLineIndentSize = Array.from(thisLineIndent)
1320-
.reduce((n, c) => n + (c === '\t' ? tabSize : 1), 0);
1321-
const insertingOnFirstLineOfInvocation = invocation.line === insertPos.line;
1322-
const freshLineIndentSize = insertingOnFirstLineOfInvocation
1323-
? thisLineIndentSize + indentSize
1324-
: thisLineIndentSize;
1325-
let tabs;
1326-
let spaces;
1327-
if (insertSpaces) {
1328-
tabs = 0;
1329-
spaces = freshLineIndentSize;
1330-
} else {
1331-
spaces = freshLineIndentSize % tabSize;
1332-
tabs = Math.floor(freshLineIndentSize / tabSize);
13331350
}
1334-
const freshLineIndent = '\t'.repeat(tabs) + ' '.repeat(spaces);
1335-
return `\n${freshLineIndent}`;
1351+
1352+
const spaces = !!maxSpaces;
1353+
1354+
if (spaces && tabs) {
1355+
return {
1356+
tabSize: maxSpaces + minSpaces,
1357+
indentSize: minSpaces,
1358+
insertSpaces: false
1359+
};
1360+
}
1361+
if (spaces && !tabs) {
1362+
return {
1363+
tabSize,
1364+
indentSize: minSpaces,
1365+
insertSpaces: true
1366+
};
1367+
}
1368+
if (!spaces && tabs) {
1369+
return {
1370+
tabSize,
1371+
indentSize,
1372+
insertSpaces: false
1373+
};
1374+
}
1375+
1376+
// document contained no indented lines, fall back to workspace settings
1377+
return {
1378+
tabSize,
1379+
indentSize,
1380+
insertSpaces
1381+
};
1382+
}
1383+
1384+
/**
1385+
* Get the IndentConfig from the workspace configuration
1386+
*/
1387+
function indentSettings(document: vscode.TextDocument, languageId: string = 'cmake'): IndentConfig {
1388+
const config = vscode.workspace.getConfiguration(
1389+
'editor', { uri: document.uri, languageId });
1390+
const tabSize = config.get<number>('tabSize', 8);
1391+
const indentSizeRaw = config.get<number|'tabSize'>('indentSize', 4);
1392+
const indentSize = indentSizeRaw === 'tabSize' ? tabSize : indentSizeRaw;
1393+
const insertSpaces = config.get<boolean>('insertSpaces', false);
1394+
1395+
return { tabSize, indentSize, insertSpaces };
1396+
}
1397+
1398+
function* documentLines(document: vscode.TextDocument): Generator<vscode.TextLine> {
1399+
for (let i = 0; i < document.lineCount; i++) {
1400+
yield document.lineAt(i);
1401+
}
13361402
}
13371403

13381404
function compareSortKeys(aKeys: (number|string)[], bKeys: (number|string)[]): number {

0 commit comments

Comments
 (0)