console amenities
authorhgn <hgodden00@gmail.com>
Mon, 16 Jan 2023 03:27:15 +0000 (03:27 +0000)
committerhgn <hgodden00@gmail.com>
Mon, 16 Jan 2023 03:27:15 +0000 (03:27 +0000)
vg_console.h

index e18515d45cd029e3d422da5c09881994193a7fc0..8f8517cb7d41662a7afce1f31ca2e9c1d97d4fce 100644 (file)
@@ -56,9 +56,23 @@ struct vg_console
        }
        functions[ 32 ];
 
+   struct 
+   {
+      const char *str;
+      int len;
+
+      u32 lev_score;
+   }
+   suggestions[12];
+   u32 suggestion_count;
+   int suggestion_select,
+       suggestion_pastepos,
+       suggestion_maxlen;
+
    u32 convar_count, function_count;
        
-       char input[96];
+       char input[96],
+        input_copy[96];
        int cursor_user, cursor_pos, string_length;
        
        char history[32][96];
@@ -136,9 +150,12 @@ VG_STATIC void _vg_console_draw( void )
        vg_uictx.cursor[3] = log_lines*fh;
        ui_fill_x();
        
+   /* 
+    * log
+    */
        ui_new_node();
        {
-               ui_fill_rect( vg_uictx.cursor, 0x77333333 );
+               ui_fill_rect( vg_uictx.cursor, 0x77181818 );
        
                vg_uictx.cursor[3] = fh;
                ui_align_bottom();
@@ -157,12 +174,14 @@ VG_STATIC void _vg_console_draw( void )
        }
        ui_end_down();
        
+       
+   /* Input area */
        vg_uictx.cursor[1] += 2;
        vg_uictx.cursor[3] = fh;
-       
+
        ui_new_node();
        {
-               ui_fill_rect( vg_uictx.cursor, 0x77333333 );
+               ui_fill_rect( vg_uictx.cursor, 0x77111111 );
                ui_text( vg_uictx.cursor, vg_console.input, 1, 0 );
                
                int start = VG_MIN( vg_console.cursor_pos, vg_console.cursor_user ),
@@ -175,6 +194,33 @@ VG_STATIC void _vg_console_draw( void )
                ui_fill_rect( vg_uictx.cursor, 0x66ffffff );
        }
        ui_end_down();
+
+
+
+   /* suggestions */
+   if( vg_console.suggestion_count )
+   {
+      vg_uictx.cursor[1] += 2;
+      vg_uictx.cursor[3] = vg_console.suggestion_count * fh;
+      vg_uictx.cursor[2] = UI_GLYPH_SPACING_X * vg_console.suggestion_maxlen;
+
+      ui_new_node();
+      {
+         ui_fill_rect( vg_uictx.cursor, 0x77040404 );
+
+         vg_uictx.cursor[3] = fh;
+         for( int i=0; i<vg_console.suggestion_count; i ++ )
+         {
+            if( i == vg_console.suggestion_select )
+               ui_fill_rect( vg_uictx.cursor, 0x66a0e508 );
+
+            ui_text( vg_uictx.cursor, vg_console.suggestions[i].str, 1, 0 );
+            vg_uictx.cursor[1] += fh;
+         }
+      }
+      ui_end_down();
+   }
+
    SDL_AtomicUnlock( &log_print_sl );
 }
 
@@ -384,6 +430,162 @@ VG_STATIC void vg_execute_console_input( const char *cmd )
        vg_error( "No command/var named '%s'. Use 'list' to view all\n", args[0] );
 }
 
+u32 str_lev_distance( const char *s1, const char *s2 )
+{
+   u32 m = strlen( s1 ),
+       n = strlen( s2 );
+
+   if( m==0 ) return n;
+   if( n==0 ) return m;
+
+   assert( n+1 <= 256 );
+
+   u32 costs[ 256 ];
+
+   for( u32 k=0; k<=n; k++ ) 
+      costs[k] = k;
+
+   u32 i = 0;
+   for( u32 i=0; i<m; i++ )
+   {
+      costs[0] = i+1;
+
+      u32 corner = i;
+
+      for( u32 j=0; j<n; j++ )
+      {
+         u32 upper = costs[j+1];
+
+         if( s1[i] == s2[j] )
+            costs[ j+1 ] = corner;
+         else
+         {
+            u32 t = (upper < corner)? upper: corner;
+            costs[j+1] = ((costs[j] < t)? costs[j]: t) + 1;
+         }
+
+         corner = upper;
+      }
+   }
+
+   return costs[n];
+}
+
+u32 str_lcs( const char *s1, const char *s2 )
+{
+   u32 m = VG_MIN( 31, strlen( s1 ) ),
+       n = VG_MIN( 31, strlen( s2 ) );
+
+   int suff[32][32],
+       result = 0;
+
+   for( int i=0; i<=m; i++ )
+   {
+      for( int j=0; j<=n; j++ )
+      {
+         if( i == 0 || j == 0 )
+            suff[i][j] = 0;
+         else if( s1[i-1] == s2[j-1] )
+         {
+            suff[i][j] = suff[i-1][j-1] + 1;
+            result = VG_MAX( result, suff[i][j] );
+         }
+         else
+            suff[i][j] = 0;
+      }
+   }
+   
+   return result;
+}
+
+/* str must not fuckoff ever! */
+VG_STATIC void console_suggest_score_text( const char *input, const char *str )
+{
+   /* calc score */
+   u32 score = str_lcs( str, input );
+
+   if( score < 1 )
+      return;
+
+   int best_pos = vg_console.suggestion_count;
+   for( int j=best_pos-1; j>=0; j -- )
+      if( score > vg_console.suggestions[j].lev_score )
+         best_pos = j;
+   
+   /* insert if good score */
+   if( best_pos < vg_list_size( vg_console.suggestions ) )
+   {
+      int start = VG_MIN( vg_console.suggestion_count, 
+                          vg_list_size( vg_console.suggestions )-1 );
+      for( int j=start; j>best_pos; j -- )
+         vg_console.suggestions[j] = vg_console.suggestions[j-1];
+
+      vg_console.suggestions[ best_pos ].str = str;
+      vg_console.suggestions[ best_pos ].len = strlen( str );
+      vg_console.suggestions[ best_pos ].lev_score = score;
+
+      if( vg_console.suggestion_count < 
+            vg_list_size( vg_console.suggestions ) )
+         vg_console.suggestion_count ++;
+   }
+}
+
+VG_STATIC void console_update_suggestions(void)
+{
+   vg_console.suggestion_count = 0;
+   vg_console.suggestion_select = -1;
+   vg_console.suggestion_maxlen = 0;
+
+   /* find current term */
+   int start_index = 0;
+
+   for( int i=0; 1; i++ )
+   {
+      if( !vg_console.input[i] )
+         break;
+
+      if( isspace( vg_console.input[i] ) )
+      {
+         start_index = i;
+
+         /* TODO:  not just functions
+          */
+
+         return;
+      }
+   }
+
+   vg_console.suggestion_pastepos = start_index;
+   const char *input_ref = &vg_console.input[ start_index ];
+
+   /* Score all  our commands and cvars */
+   for( int i=0; i<vg_console.convar_count; i++ )
+   {
+      vg_convar *cvar = &vg_console.convars[i];
+      console_suggest_score_text( input_ref, cvar->name );
+   }
+
+   for( int i=0; i<vg_console.function_count; i++ )
+   {
+      vg_cmd *cmd = &vg_console.functions[i];
+      console_suggest_score_text( input_ref, cmd->name );
+   }
+
+   /* some post processing */
+   for( int i=0; i<vg_console.suggestion_count; i++ )
+   {
+      vg_console.suggestion_maxlen = VG_MAX( vg_console.suggestion_maxlen,
+                                             vg_console.suggestions[i].len );
+
+      if( vg_console.suggestions[i].lev_score <
+          vg_console.suggestions[0].lev_score/2 )
+      {
+         vg_console.suggestion_count = i;
+         return;
+      }
+   }
+}
+
 /*
  * Console Interface
  */
@@ -394,12 +596,12 @@ VG_STATIC void console_make_selection( int* start, int* end )
 }
 
 VG_STATIC void console_move_cursor( int* cursor0, int* cursor1, 
-      int dir, int snap_together )
+                                    int dir, int snap_together )
 {
        *cursor0 = VG_MAX( 0, vg_console.cursor_user + dir );
        *cursor0 = 
       VG_MIN( 
-         VG_MIN( vg_list_size( vg_console.input ), strlen( vg_console.input )), 
+         VG_MIN( vg_list_size(vg_console.input)-1, strlen( vg_console.input )), 
       *cursor0 );
 
        if( snap_together ) 
@@ -408,10 +610,10 @@ VG_STATIC void console_move_cursor( int* cursor0, int* cursor1,
 
 VG_STATIC int console_makeroom( int datastart, int length )
 {
-       int move_to = VG_MIN( datastart+length, vg_list_size( vg_console.input ) );
+       int move_to = VG_MIN( datastart+length, vg_list_size( vg_console.input )-1 );
        int move_amount = strlen( vg_console.input )-datastart;
        int move_end = 
-      VG_MIN( move_to+move_amount, vg_list_size( vg_console.input ) );
+      VG_MIN( move_to+move_amount, vg_list_size( vg_console.input )-1 );
        move_amount = move_end-move_to;
        
        if( move_amount )
@@ -421,7 +623,7 @@ VG_STATIC int console_makeroom( int datastart, int length )
        
        vg_console.input[ move_end ] = '\0';
        
-       return VG_MIN( length, vg_list_size( vg_console.input )-datastart );
+       return VG_MIN( length, vg_list_size( vg_console.input )-datastart-1 );
 }
 
 VG_STATIC int console_delete_char( int direction )
@@ -445,6 +647,8 @@ VG_STATIC int console_delete_char( int direction )
        memmove( &vg_console.input[ start ], 
             &vg_console.input[ end ], 
             remaining_length );
+
+   console_update_suggestions();
        return start;
 }
 
@@ -479,8 +683,9 @@ VG_STATIC void console_clipboard_paste(void)
        memcpy( vg_console.input + datastart, text, cpylength);
        console_move_cursor( &vg_console.cursor_user, 
                         &vg_console.cursor_pos, cpylength, 1 );
-   
    SDL_free( text );
+
+   console_update_suggestions();
 }
 
 VG_STATIC void console_put_char( char c )
@@ -542,7 +747,7 @@ VG_STATIC void _console_down(void)
 
    console_move_cursor( &vg_console.cursor_user, 
                         &vg_console.cursor_pos, 
-                        vg_list_size( vg_console.input ), 1 );
+                        vg_list_size(vg_console.input)-1, 1 );
 }
 
 VG_STATIC void _console_up(void)
@@ -564,7 +769,7 @@ VG_STATIC void _console_up(void)
    console_history_get( vg_console.input, vg_console.history_pos );
    console_move_cursor( &vg_console.cursor_user, 
                         &vg_console.cursor_pos, 
-                        vg_list_size( vg_console.input ), 1);
+                        vg_list_size(vg_console.input)-1, 1);
 }
 
 VG_STATIC void _console_backspace(void)
@@ -599,13 +804,13 @@ VG_STATIC void _console_end(void)
 {
    console_move_cursor( &vg_console.cursor_user, 
                         &vg_console.cursor_pos, 
-                        vg_list_size( vg_console.input ), 1 );
+                        vg_list_size(vg_console.input)-1, 1 );
 }
 
 VG_STATIC void _console_select_all(void)
 {
-   console_move_cursor( &vg_console.cursor_user, NULL, 10000, 0);
-   console_move_cursor( &vg_console.cursor_pos, NULL, -10000, 0);
+   console_move_cursor( &vg_console.cursor_user, NULL,  10000, 0);
+   console_move_cursor( &vg_console.cursor_pos,  NULL, -10000, 0);
 }
 
 VG_STATIC void _console_cut(void)
@@ -639,8 +844,72 @@ VG_STATIC void _console_enter(void)
    console_move_cursor( &vg_console.cursor_user, 
                         &vg_console.cursor_pos, -10000, 1 );
    vg_console.input[0] = '\0';
+
+   console_update_suggestions();
 }
 
+/*
+ * Suggestion controls
+ */
+VG_STATIC void _console_fetch_suggestion(void)
+{
+   if( vg_console.suggestion_select == -1 )
+   {
+      strcpy( vg_console.input, vg_console.input_copy );
+      console_move_cursor( &vg_console.cursor_user, 
+                           &vg_console.cursor_pos, 10000, 1 );
+   }
+   else
+   {
+      strncpy( vg_console.input,
+            vg_console.suggestions[ vg_console.suggestion_select ].str,
+            vg_list_size( vg_console.input )-1 );
+
+      console_move_cursor( &vg_console.cursor_user, 
+                           &vg_console.cursor_pos, 10000, 1 );
+      console_put_char( ' ' ); 
+   }
+}
+
+VG_STATIC void _console_suggest_store_normal(void)
+{
+   if( vg_console.suggestion_select == -1 )
+      strcpy( vg_console.input_copy, vg_console.input );
+}
+
+VG_STATIC void _console_suggest_next(void)
+{
+   if( vg_console.suggestion_count )
+   {
+      _console_suggest_store_normal();
+
+      vg_console.suggestion_select ++;
+
+      if( vg_console.suggestion_select >= vg_console.suggestion_count )
+         vg_console.suggestion_select = -1;
+      
+      _console_fetch_suggestion();
+   }
+}
+
+VG_STATIC void _console_suggest_prev(void)
+{
+   if( vg_console.suggestion_count )
+   {
+      _console_suggest_store_normal();
+
+      vg_console.suggestion_select --;
+
+      if( vg_console.suggestion_select < -1 )
+         vg_console.suggestion_select = vg_console.suggestion_count-1;
+
+      _console_fetch_suggestion();
+   }
+}
+
+/*
+ * Handles binds
+ */
 VG_STATIC void console_proc_key( SDL_Keysym ev )
 {
    /* Open / close console */
@@ -681,10 +950,21 @@ VG_STATIC void console_proc_key( SDL_Keysym ev )
       { KMOD_CTRL,      SDLK_c,          console_to_clipboard       },
       { KMOD_CTRL,      SDLK_x,          _console_cut               },
       { KMOD_CTRL,      SDLK_v,          console_clipboard_paste    },
-      { 0,              SDLK_RETURN,     _console_enter             }
+      { 0,              SDLK_RETURN,     _console_enter             },
+      { KMOD_CTRL,      SDLK_n,          _console_suggest_next      },
+      { KMOD_CTRL,      SDLK_p,          _console_suggest_prev      }
    };
 
-   SDL_Keymod mod = ev.mod & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT);
+   SDL_Keymod mod = 0;
+
+   if( ev.mod & KMOD_SHIFT )
+      mod |= KMOD_SHIFT;
+
+   if( ev.mod & KMOD_CTRL )
+      mod |= KMOD_CTRL;
+
+   if( ev.mod & KMOD_ALT )
+      mod |= KMOD_ALT;
 
    for( int i=0; i<vg_list_size( mappings ); i++ )
    {
@@ -709,6 +989,9 @@ VG_STATIC void console_proc_key( SDL_Keysym ev )
    }
 }
 
+/*
+ * Callback for text entry mode
+ */
 VG_STATIC void console_proc_utf8( const char *text )
 {
    const char *ptr = text;
@@ -719,6 +1002,8 @@ VG_STATIC void console_proc_utf8( const char *text )
          console_put_char( *ptr );
       ptr ++;
    }
+
+   console_update_suggestions();
 }
 
 #endif /* VG_CONSOLE_H */