Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to the **VS Code Aster** extension will be documented in thi
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.8.1] - 2026-04-20

Follow-up polish on the `.export` editor form: smarter autocomplete suggestions and the saved file now opens automatically after create/save.

### Added
- **Type-aware autocomplete** — file name suggestions in the export form are now filtered by the selected F-line type so irrelevant files are hidden:
- `mmed` / `rmed` rows accept `.med`, `.mmed`, `.rmed`, and any custom extension registered in `vs-code-aster.medFileExtensions` (so user-added MED extensions like `.71` show up).
- `comm` rows accept `.com*` (covers `.comm`, `.com0`, `.com1`, ...).
- `mess`, `msh`, and `dat` rows require a literal extension match.
- Types without a conventional extension (`mail`, `base`, `libr`, `tab`, `nom`) still accept any file.
- **Autocomplete on output rows** — output file name fields now use the same autocomplete dropdown as inputs, making it easy to reuse existing file names for outputs.
- **Reveal on save** — creating or saving an `.export` file from the form now opens the file in a text editor (or focuses the existing tab if it's already open), so you immediately see the formatted result. Stale tabs left over from a rename are closed automatically.

### Fixed
- The hidden `.vs-code-aster/` folder no longer appears in the export form's autocomplete suggestions.

## [1.8.0] - 2026-04-20

Full rewrite of the `.export` form in Svelte + Tailwind, first-class language support for `.export` files (syntax highlighting, formatter, format-on-save), and a batch of UX upgrades.
Expand Down
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cff-version: 1.8.0
cff-version: 1.8.1
title: VS Code Aster
message: >-
If you use this software, please cite it using the
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align="center"><img src="https://raw.githubusercontent.com/simvia-tech/vs-code-aster/main/media/images/simvia.png" alt="Simvia Logo" width="50%" /></p>

<p align="center">
<a href="/"><img src="https://img.shields.io/badge/version-1.8.0-blue" alt="Version" /></a>
<a href="/"><img src="https://img.shields.io/badge/version-1.8.1-blue" alt="Version" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-GPL%203.0-green" alt="License" /></a>
</p>

Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

The extension aims to reduce friction between modeling, validation, execution, and analysis by bringing **code_aster** native workflows into the editor.

## Current Capabilities (v1.8.0)
## Current Capabilities (v1.8.1)

- `.export` file generator
- 3D mesh viewer
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vs-code-aster",
"displayName": "VS Code Aster",
"version": "1.8.0",
"version": "1.8.1",
"description": "VS Code extension for code_aster",
"publisher": "simvia",
"license": "GPL-3.0",
Expand Down
64 changes: 62 additions & 2 deletions src/ExportEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as path from 'path';
import * as fs from 'fs';
import { sendTelemetry, TelemetryType } from './telemetry';
import { formatExportContent } from './ExportFormatter';
import { STATIC_MED_EXTS } from './MedEditorProvider';

interface ExportDescriptor {
filename: string;
Expand Down Expand Up @@ -37,6 +38,36 @@ export interface ExportEditorResult {
name: string;
}

/**
* Returns true if `filename` is a plausible match for an .export F-line
* `type`. Types without a conventional extension (mail, base, libr, tab, nom)
* match anything so the user can still pick arbitrary files.
*/
function matchesExportType(filename: string, type: string): boolean {
const lower = filename.toLowerCase();
const ext = path.extname(lower);

if (type === 'mmed' || type === 'rmed') {
if (STATIC_MED_EXTS.has(ext)) {
return true;
}
const configured = vscode.workspace
.getConfiguration('vs-code-aster')
.get<string[]>('medFileExtensions', ['.med', '.mmed', '.rmed']);
return configured.some((e) => e.toLowerCase() === ext);
}

if (type === 'comm') {
return /\.com[a-z0-9]*$/.test(lower);
}

if (type === 'mess' || type === 'msh' || type === 'dat') {
return ext === `.${type}`;
}

return true;
}

/**
* Provides basic dialog semantics over a VS Code webview panel for export file creation and editing.
* @template TResult The result type returned from the dialog.
Expand Down Expand Up @@ -190,8 +221,10 @@ export class ExportEditor<TResult> implements vscode.Disposable {
const fullPath = path.join(this.destinationFolder, filename);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, content, 'utf8');
let renamedFrom: string | undefined;
if (this.originalFilename && this.originalFilename !== filename) {
const oldPath = path.join(this.destinationFolder, this.originalFilename);
renamedFrom = oldPath;
try {
fs.unlinkSync(oldPath);
} catch {
Expand All @@ -201,6 +234,7 @@ export class ExportEditor<TResult> implements vscode.Disposable {

void sendTelemetry(TelemetryType.EXPORT_SAVED);

void this.revealExportFile(fullPath, renamedFrom);
this.panel.dispose();
} else if (message.command === 'autocomplete') {
const suggestions = this.getMatchingFiles(message.value, message.type);
Expand All @@ -224,12 +258,38 @@ export class ExportEditor<TResult> implements vscode.Disposable {
return this.webview.asWebviewUri(vscode.Uri.file(fullPath)).toString();
}

private getMatchingFiles(partial: string, _type: string): string[] {
/**
* Open the saved `.export` file in a text editor, reusing an existing tab
* when the file is already open. If the file was renamed, the stale tab
* for the old path is closed first so we don't leave a dangling editor.
*/
private async revealExportFile(newPath: string, renamedFrom?: string): Promise<void> {
if (renamedFrom) {
const staleTabs: vscode.Tab[] = [];
for (const group of vscode.window.tabGroups.all) {
for (const tab of group.tabs) {
if (tab.input instanceof vscode.TabInputText && tab.input.uri.fsPath === renamedFrom) {
staleTabs.push(tab);
}
}
}
if (staleTabs.length > 0) {
await vscode.window.tabGroups.close(staleTabs);
}
}
await vscode.window.showTextDocument(vscode.Uri.file(newPath));
}

private getMatchingFiles(partial: string, type: string): string[] {
if (!this.destinationFolder) {
return [];
}
const allFiles = fs.readdirSync(this.destinationFolder);
return allFiles.filter((name) => name.toLowerCase().includes(partial.toLowerCase()));
const needle = partial.toLowerCase();
return allFiles
.filter((name) => name !== '.vs-code-aster')
.filter((name) => matchesExportType(name, type))
.filter((name) => name.toLowerCase().includes(needle));
}

/** Parse the content of an export file into structured form data. */
Expand Down
30 changes: 9 additions & 21 deletions webviews/export/src/components/FileRow.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -111,27 +111,15 @@
</button>
</Dropdown>

{#if kind === 'input'}
<AutocompleteInput
id={`name-${file.id}`}
bind:value={file.name}
placeholder="File name"
invalid={!!nameError}
suggestions={suggestionsFor[file.id] ?? []}
onQuery={handleNameQuery}
onFocusChange={(focused) => onAutocompleteFocus(focused ? file.id : null)}
/>
{:else}
<input
id={`name-${file.id}`}
type="text"
aria-label="File name"
class="flex-1 min-w-0 bg-ui-input-bg text-ui-input-fg border border-ui-input-border rounded-sm px-2 py-1 text-sm focus:border-ui-focus focus:outline-none"
class:input-warning={!!nameError}
placeholder="File name"
bind:value={file.name}
/>
{/if}
<AutocompleteInput
id={`name-${file.id}`}
bind:value={file.name}
placeholder="File name"
invalid={!!nameError}
suggestions={suggestionsFor[file.id] ?? []}
onQuery={handleNameQuery}
onFocusChange={(focused) => onAutocompleteFocus(focused ? file.id : null)}
/>

<input
id={`unit-${file.id}`}
Expand Down
Loading