@@ -19,6 +19,7 @@ pub(super) const BLOCK_COMMANDS: &[&str] = &["for", "while", "loop", "module"];
1919pub ( super ) const CONDITIONAL_COMMANDS : & [ & str ] = & [ "if" , "try" ] ;
2020pub ( super ) const DEF_COMMANDS : & [ & str ] = & [ "def" , "def-env" , "export def" ] ;
2121pub ( super ) const EXTERN_COMMANDS : & [ & str ] = & [ "extern" , "export extern" ] ;
22+ pub ( super ) const ALIAS_COMMANDS : & [ & str ] = & [ "alias" , "export alias" ] ;
2223pub ( super ) const LET_COMMANDS : & [ & str ] = & [ "let" , "let-env" , "mut" , "const" , "export const" ] ;
2324
2425impl < ' a > Formatter < ' a > {
@@ -47,6 +48,11 @@ impl<'a> Formatter<'a> {
4748 return ;
4849 }
4950
51+ if matches ! ( cmd_type, CommandType :: Alias ) {
52+ self . format_alias_call ( call) ;
53+ return ;
54+ }
55+
5056 if decl_name == "for" {
5157 self . format_for_call ( call) ;
5258 return ;
@@ -270,12 +276,90 @@ impl<'a> Formatter<'a> {
270276 }
271277 }
272278
279+ /// Format alias definitions while preserving the literal right-hand side.
280+ ///
281+ /// The parser resolves alias references semantically, which can expand the
282+ /// RHS if it is re-rendered from the AST. Preserve the original source text
283+ /// after `=` to keep alias definitions idempotent.
284+ pub ( super ) fn format_alias_call ( & mut self , call : & nu_protocol:: ast:: Call ) {
285+ let positional: Vec < & Expression > = call
286+ . arguments
287+ . iter ( )
288+ . filter_map ( |arg| match arg {
289+ Argument :: Positional ( expr) | Argument :: Unknown ( expr) => Some ( expr) ,
290+ _ => None ,
291+ } )
292+ . collect ( ) ;
293+
294+ let Some ( name) = positional. first ( ) else {
295+ for arg in & call. arguments {
296+ self . format_call_argument ( arg, & CommandType :: Alias ) ;
297+ }
298+ return ;
299+ } ;
300+
301+ self . space ( ) ;
302+ self . format_expression ( name) ;
303+
304+ let rhs_end = call
305+ . arguments
306+ . iter ( )
307+ . filter_map ( |arg| match arg {
308+ Argument :: Positional ( expr) | Argument :: Unknown ( expr) | Argument :: Spread ( expr) => {
309+ Some ( expr. span . end )
310+ }
311+ Argument :: Named ( named) => named
312+ . 2
313+ . as_ref ( )
314+ . map_or ( Some ( named. 0 . span . end ) , |value| Some ( value. span . end ) ) ,
315+ } )
316+ . max ( ) ;
317+
318+ let Some ( rhs_end) = rhs_end else {
319+ return ;
320+ } ;
321+
322+ if name. span . end >= rhs_end || rhs_end > self . source . len ( ) {
323+ self . format_regular_arguments ( & call. arguments [ 1 ..] ) ;
324+ return ;
325+ }
326+
327+ let between = & self . source [ name. span . end ..rhs_end] ;
328+ let Some ( eq_offset) = between. iter ( ) . position ( |byte| * byte == b'=' ) else {
329+ self . format_regular_arguments ( & call. arguments [ 1 ..] ) ;
330+ return ;
331+ } ;
332+
333+ let rhs_start = between[ eq_offset + 1 ..]
334+ . iter ( )
335+ . position ( |byte| !byte. is_ascii_whitespace ( ) )
336+ . map ( |offset| name. span . end + eq_offset + 1 + offset) ;
337+
338+ let Some ( rhs_start) = rhs_start else {
339+ self . write ( " =" ) ;
340+ return ;
341+ } ;
342+
343+ // Keep the exact user-authored alias RHS to avoid semantic expansion
344+ // when aliases reference other aliases.
345+ self . write ( " = " ) ;
346+ self . write_bytes ( & self . source [ rhs_start..rhs_end] ) ;
347+ }
348+
349+ fn format_regular_arguments ( & mut self , args : & [ Argument ] ) {
350+ for arg in args {
351+ self . format_call_argument ( arg, & CommandType :: Regular ) ;
352+ }
353+ }
354+
273355 /// Classify a command name into a [`CommandType`] for formatting purposes.
274356 pub ( super ) fn classify_command ( name : & str ) -> CommandType {
275357 if DEF_COMMANDS . contains ( & name) {
276358 CommandType :: Def
277359 } else if EXTERN_COMMANDS . contains ( & name) {
278360 CommandType :: Extern
361+ } else if ALIAS_COMMANDS . contains ( & name) {
362+ CommandType :: Alias
279363 } else if CONDITIONAL_COMMANDS . contains ( & name) {
280364 CommandType :: Conditional
281365 } else if LET_COMMANDS . contains ( & name) {
@@ -336,6 +420,7 @@ impl<'a> Formatter<'a> {
336420 match cmd_type {
337421 CommandType :: Def => self . format_def_argument ( positional) ,
338422 CommandType :: Extern => self . format_extern_argument ( positional) ,
423+ CommandType :: Alias => self . format_expression ( positional) ,
339424 CommandType :: Conditional => {
340425 self . conditional_context_depth += 1 ;
341426 self . format_block_or_expr ( positional) ;
0 commit comments