Skip to content

Commit 1bc2dad

Browse files
committed
Add toggle line comment command (issue #27)
Add an OPM Flow: Toggle Line Comment command that inserts or removes a '--' marker at the absolute beginning of each selected line. If every non-blank selected line is already commented the marker is removed; otherwise '-- ' is prefixed at column 0. Blank lines are left untouched. The toggle logic lives in a pure toggleLineComments() helper with unit tests. The command is exposed via the editor context menu and bound to Ctrl+/ (Cmd+/ on macOS), which VS Code resolves through the active keyboard layout.
1 parent 0118992 commit 1bc2dad

5 files changed

Lines changed: 151 additions & 1 deletion

File tree

vscode-extension/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,22 @@ VFPIDIMS
172172
30 20 20 /
173173
```
174174

175+
### Toggle Line Comment
176+
177+
Select one or more lines and invoke **OPM Flow: Toggle Line Comment** (bound to
178+
`Ctrl+/`, or `Cmd+/` on macOS — VS Code maps this to the same physical key as the
179+
built-in comment toggle, so it follows your keyboard layout) — also available from
180+
the right-click menu — to
181+
add or remove a `--` comment marker at the very start of each selected line. If
182+
every non-blank line in the selection is already commented the marker is
183+
removed; otherwise `-- ` is inserted at column 0 of each line. Blank lines are
184+
left untouched.
185+
186+
```
187+
-- WCONPROD
188+
-- 'PROD' 'OPEN' /
189+
```
190+
175191
### File Navigation (INCLUDE / IMPORT / RESTART / GDFILE)
176192

177193
Quoted file paths on `INCLUDE`, `IMPORT`, `RESTART`, and `GDFILE` statements

vscode-extension/package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@
130130
{
131131
"command": "opm-flow.addColumnHeaders",
132132
"title": "OPM Flow: Add Column Headers"
133+
},
134+
{
135+
"command": "opm-flow.toggleLineComment",
136+
"title": "OPM Flow: Toggle Line Comment"
133137
}
134138
],
135139
"menus": {
@@ -143,9 +147,22 @@
143147
"command": "opm-flow.addColumnHeaders",
144148
"when": "resourceLangId == opm-flow",
145149
"group": "opm-flow"
150+
},
151+
{
152+
"command": "opm-flow.toggleLineComment",
153+
"when": "resourceLangId == opm-flow",
154+
"group": "opm-flow"
146155
}
147156
]
148157
},
158+
"keybindings": [
159+
{
160+
"command": "opm-flow.toggleLineComment",
161+
"key": "ctrl+/",
162+
"mac": "cmd+/",
163+
"when": "editorTextFocus && !editorReadonly && editorLangId == opm-flow"
164+
}
165+
],
149166
"configuration": {
150167
"title": "OPM Flow",
151168
"properties": {

vscode-extension/src/extension.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
formatRecordGroupWithHeading,
1818
buildHeadingAndAlignedRecords,
1919
tokenColumnCount,
20+
toggleLineComments,
2021
} from './formatting';
2122
import { computeDiagnostics } from './analysis';
2223
import { findFileReferences } from './links';
@@ -1170,6 +1171,37 @@ export function activate(context: vscode.ExtensionContext): void {
11701171
}
11711172
});
11721173

1174+
// --- Command: toggle line comment (`--` at the absolute start of line) ---
1175+
const toggleCommentCommand = vscode.commands.registerCommand('opm-flow.toggleLineComment', async () => {
1176+
const editor = vscode.window.activeTextEditor;
1177+
if (!editor) return;
1178+
const doc = editor.document;
1179+
1180+
// Toggle every line touched by any selection (deduplicated). A decision
1181+
// (comment vs. uncomment) is made independently per contiguous selection
1182+
// so each behaves like the editor's native toggle.
1183+
await editor.edit(b => {
1184+
for (const sel of editor.selections) {
1185+
const firstLine = sel.start.line;
1186+
// An empty trailing line in the selection (cursor at column 0 of the
1187+
// line after the last selected character) should not be included.
1188+
const lastLine = sel.end.line > sel.start.line && sel.end.character === 0
1189+
? sel.end.line - 1
1190+
: sel.end.line;
1191+
const originals: string[] = [];
1192+
for (let ln = firstLine; ln <= lastLine; ln++) {
1193+
originals.push(doc.lineAt(ln).text);
1194+
}
1195+
const toggled = toggleLineComments(originals);
1196+
if (!toggled) continue;
1197+
for (let k = 0; k < toggled.length; k++) {
1198+
if (toggled[k] === originals[k]) continue;
1199+
b.replace(doc.lineAt(firstLine + k).range, toggled[k]);
1200+
}
1201+
}
1202+
});
1203+
});
1204+
11731205
const alignColumnsCommand = vscode.commands.registerCommand('opm-flow.alignRecordColumns', async () => {
11741206
const editor = vscode.window.activeTextEditor;
11751207
if (!editor) return;
@@ -1215,7 +1247,7 @@ export function activate(context: vscode.ExtensionContext): void {
12151247
}),
12161248
);
12171249

1218-
context.subscriptions.push(completionProvider, valueCompletionProvider, hoverProvider, generateReferenceCommand, addColumnHeadersCommand, alignColumnsCommand, fileLinkProvider, foldingProvider);
1250+
context.subscriptions.push(completionProvider, valueCompletionProvider, hoverProvider, generateReferenceCommand, addColumnHeadersCommand, alignColumnsCommand, toggleCommentCommand, fileLinkProvider, foldingProvider);
12191251
}
12201252

12211253
export function deactivate(): void {}

vscode-extension/src/formatting.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
formatRecordGroupWithHeading,
1010
buildHeadingAndAlignedRecords,
1111
tokenColumnCount,
12+
toggleLineComments,
1213
RecordLine,
1314
} from './formatting';
1415

@@ -699,3 +700,58 @@ describe('columnForCompletion', () => {
699700
expect(columnForCompletion('', 0)).toBe(1);
700701
});
701702
});
703+
704+
// ---------------------------------------------------------------------------
705+
// toggleLineComments
706+
// ---------------------------------------------------------------------------
707+
708+
describe('toggleLineComments', () => {
709+
test('comments uncommented lines at column 0', () => {
710+
expect(toggleLineComments(['WCONPROD', ' PROD OPEN'])).toEqual([
711+
'-- WCONPROD',
712+
'-- PROD OPEN',
713+
]);
714+
});
715+
716+
test('uncomments when every non-blank line is commented', () => {
717+
expect(toggleLineComments(['-- WCONPROD', '-- PROD OPEN'])).toEqual([
718+
'WCONPROD',
719+
' PROD OPEN',
720+
]);
721+
});
722+
723+
test('removes only one space after the marker on uncomment', () => {
724+
expect(toggleLineComments(['-- double space'])).toEqual([' double space']);
725+
expect(toggleLineComments(['--no space'])).toEqual(['no space']);
726+
});
727+
728+
test('comments all when the block is only partially commented', () => {
729+
expect(toggleLineComments(['-- already', 'not yet'])).toEqual([
730+
'-- -- already',
731+
'-- not yet',
732+
]);
733+
});
734+
735+
test('leaves blank lines untouched and ignores them in the decision', () => {
736+
expect(toggleLineComments(['-- one', '', '-- two'])).toEqual([
737+
'one',
738+
'',
739+
'two',
740+
]);
741+
});
742+
743+
test('an indented marker is not treated as commented', () => {
744+
expect(toggleLineComments([' -- indented'])).toEqual(['-- -- indented']);
745+
});
746+
747+
test('round-trips comment then uncomment', () => {
748+
const original = ['RUNSPEC', ' DIMENS', ' 10 10 3 /'];
749+
const commented = toggleLineComments(original)!;
750+
expect(toggleLineComments(commented)).toEqual(original);
751+
});
752+
753+
test('returns null when there is nothing to toggle', () => {
754+
expect(toggleLineComments([])).toBeNull();
755+
expect(toggleLineComments(['', ' '])).toBeNull();
756+
});
757+
});

vscode-extension/src/formatting.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,35 @@ export function isCommentLine(line: string): boolean {
178178
return /^\s*--/.test(line);
179179
}
180180

181+
// ---------------------------------------------------------------------------
182+
// Line-comment toggle
183+
// ---------------------------------------------------------------------------
184+
185+
/** A line is treated as commented for toggle purposes only when the comment
186+
* marker sits at the *absolute* start of the line (column 0). An indented
187+
* `--` is left alone so the toggle round-trips cleanly. */
188+
const LEADING_COMMENT_RE = /^--[ \t]?/;
189+
190+
/**
191+
* Toggle `--` line comments at the absolute beginning of each given line.
192+
*
193+
* Mirrors the editor's "toggle line comment" convention: if every non-blank
194+
* line already starts with `--`, all of them are uncommented; otherwise every
195+
* non-blank line is commented by prefixing `-- ` at column 0. Blank lines are
196+
* left untouched. Returns the rewritten lines, or `null` when there is nothing
197+
* to toggle (no non-blank lines).
198+
*/
199+
export function toggleLineComments(lines: string[]): string[] | null {
200+
const nonBlank = lines.filter(l => l.trim() !== '');
201+
if (nonBlank.length === 0) return null;
202+
const allCommented = nonBlank.every(l => l.startsWith('--'));
203+
return lines.map(l => {
204+
if (l.trim() === '') return l;
205+
if (allCommented) return l.replace(LEADING_COMMENT_RE, '');
206+
return `-- ${l}`;
207+
});
208+
}
209+
181210
// ---------------------------------------------------------------------------
182211
// Column alignment helpers
183212
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)