abapgit-agent 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +261 -0
- package/CLAUDE.md +152 -16
- package/RELEASE_NOTES.md +13 -0
- package/abap/CLAUDE.md +210 -0
- package/abap/copilot-instructions.md +28 -0
- package/abap/zcl_abgagt_agent.clas.abap +2 -2
- package/abap/zcl_abgagt_command_inspect.clas.abap +255 -36
- package/abap/zcl_abgagt_command_view.clas.abap +3 -1
- package/abap/zcl_abgagt_util.clas.abap +2 -2
- package/abap/zcl_abgagt_viewer_ddls.clas.abap +83 -0
- package/abap/zcl_abgagt_viewer_ddls.clas.xml +15 -0
- package/abap/zcl_abgagt_viewer_ttyp.clas.abap +93 -0
- package/abap/zcl_abgagt_viewer_ttyp.clas.xml +15 -0
- package/abap/zif_abgagt_viewer.intf.abap +2 -1
- package/bin/abapgit-agent +210 -40
- package/docs/view-command.md +94 -2
- package/package.json +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
*"*"use source
|
|
2
|
+
*"*"Local Interface:
|
|
3
|
+
*"**********************************************************************
|
|
4
|
+
CLASS zcl_abgagt_viewer_ddls DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
|
5
|
+
|
|
6
|
+
PUBLIC SECTION.
|
|
7
|
+
INTERFACES zif_abgagt_viewer.
|
|
8
|
+
|
|
9
|
+
ENDCLASS.
|
|
10
|
+
|
|
11
|
+
CLASS zcl_abgagt_viewer_ddls IMPLEMENTATION.
|
|
12
|
+
|
|
13
|
+
METHOD zif_abgagt_viewer~get_info.
|
|
14
|
+
DATA: lv_ddls_name TYPE ddlname,
|
|
15
|
+
lo_handler TYPE REF TO if_dd_ddl_handler,
|
|
16
|
+
ls_ddlsrcv TYPE ddddlsrcv,
|
|
17
|
+
lv_devclass TYPE tadir-devclass,
|
|
18
|
+
lv_found TYPE abap_bool.
|
|
19
|
+
|
|
20
|
+
rs_info-name = iv_name.
|
|
21
|
+
rs_info-type = 'DDLS'.
|
|
22
|
+
rs_info-type_text = 'CDS View'.
|
|
23
|
+
|
|
24
|
+
" Get package from TADIR
|
|
25
|
+
SELECT SINGLE devclass FROM tadir
|
|
26
|
+
INTO lv_devclass
|
|
27
|
+
WHERE obj_name = iv_name
|
|
28
|
+
AND object = 'DDLS'.
|
|
29
|
+
IF sy-subrc = 0.
|
|
30
|
+
rs_info-description = |CDS View { iv_name } in { lv_devclass }|.
|
|
31
|
+
ELSE.
|
|
32
|
+
rs_info-not_found = abap_true.
|
|
33
|
+
RETURN.
|
|
34
|
+
ENDIF.
|
|
35
|
+
|
|
36
|
+
" Use DDL handler to read CDS view source
|
|
37
|
+
lo_handler = cl_dd_ddl_handler_factory=>create( ).
|
|
38
|
+
lv_ddls_name = iv_name.
|
|
39
|
+
|
|
40
|
+
" First try to read inactive version (get_state = 'M')
|
|
41
|
+
TRY.
|
|
42
|
+
lo_handler->read(
|
|
43
|
+
EXPORTING
|
|
44
|
+
name = lv_ddls_name
|
|
45
|
+
get_state = 'M'
|
|
46
|
+
IMPORTING
|
|
47
|
+
ddddlsrcv_wa = ls_ddlsrcv ).
|
|
48
|
+
|
|
49
|
+
IF ls_ddlsrcv-source IS NOT INITIAL.
|
|
50
|
+
lv_found = abap_true.
|
|
51
|
+
ENDIF.
|
|
52
|
+
|
|
53
|
+
CATCH cx_dd_ddl_check.
|
|
54
|
+
" Ignore - will try active version
|
|
55
|
+
ENDTRY.
|
|
56
|
+
|
|
57
|
+
" If no inactive version, try active version
|
|
58
|
+
IF lv_found = abap_false.
|
|
59
|
+
TRY.
|
|
60
|
+
lo_handler->read(
|
|
61
|
+
EXPORTING
|
|
62
|
+
name = lv_ddls_name
|
|
63
|
+
get_state = 'A'
|
|
64
|
+
IMPORTING
|
|
65
|
+
ddddlsrcv_wa = ls_ddlsrcv ).
|
|
66
|
+
|
|
67
|
+
IF ls_ddlsrcv-source IS NOT INITIAL.
|
|
68
|
+
lv_found = abap_true.
|
|
69
|
+
ENDIF.
|
|
70
|
+
CATCH cx_dd_ddl_check.
|
|
71
|
+
" Not found
|
|
72
|
+
ENDTRY.
|
|
73
|
+
ENDIF.
|
|
74
|
+
|
|
75
|
+
" Set source code if found
|
|
76
|
+
IF lv_found = abap_true.
|
|
77
|
+
rs_info-source = ls_ddlsrcv-source.
|
|
78
|
+
ELSE.
|
|
79
|
+
rs_info-not_found = abap_true.
|
|
80
|
+
ENDIF.
|
|
81
|
+
ENDMETHOD.
|
|
82
|
+
|
|
83
|
+
ENDCLASS.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
|
3
|
+
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
|
4
|
+
<asx:values>
|
|
5
|
+
<VSEOCLASS>
|
|
6
|
+
<CLSNAME>ZCL_ABGAGT_VIEWER_DDLS</CLSNAME>
|
|
7
|
+
<LANGU>E</LANGU>
|
|
8
|
+
<DESCRIPT>Viewer for CDS Views</DESCRIPT>
|
|
9
|
+
<EXPOSURE>2</EXPOSURE>
|
|
10
|
+
<STATE>1</STATE>
|
|
11
|
+
<UNICODE>X</UNICODE>
|
|
12
|
+
</VSEOCLASS>
|
|
13
|
+
</asx:values>
|
|
14
|
+
</asx:abap>
|
|
15
|
+
</abapGit>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
*"*"use source
|
|
2
|
+
*"*"Local Interface:
|
|
3
|
+
*"**********************************************************************
|
|
4
|
+
CLASS zcl_abgagt_viewer_ttyp DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
|
5
|
+
|
|
6
|
+
PUBLIC SECTION.
|
|
7
|
+
INTERFACES zif_abgagt_viewer.
|
|
8
|
+
|
|
9
|
+
ENDCLASS.
|
|
10
|
+
|
|
11
|
+
CLASS zcl_abgagt_viewer_ttyp IMPLEMENTATION.
|
|
12
|
+
|
|
13
|
+
METHOD zif_abgagt_viewer~get_info.
|
|
14
|
+
DATA: lv_obj_name TYPE tadir-obj_name,
|
|
15
|
+
lv_devclass TYPE tadir-devclass,
|
|
16
|
+
lv_linetype TYPE string,
|
|
17
|
+
lv_tabprottype TYPE string,
|
|
18
|
+
lv_keydef TYPE string,
|
|
19
|
+
lv_access_mode TYPE string,
|
|
20
|
+
lv_key_definition TYPE string.
|
|
21
|
+
|
|
22
|
+
rs_info-name = iv_name.
|
|
23
|
+
rs_info-type = 'TTYP'.
|
|
24
|
+
rs_info-type_text = 'Table Type'.
|
|
25
|
+
|
|
26
|
+
" Get package from TADIR
|
|
27
|
+
SELECT SINGLE obj_name devclass FROM tadir
|
|
28
|
+
INTO (lv_obj_name, lv_devclass)
|
|
29
|
+
WHERE obj_name = iv_name
|
|
30
|
+
AND object = 'TTYP'.
|
|
31
|
+
IF sy-subrc = 0.
|
|
32
|
+
rs_info-description = |Table Type { iv_name } in { lv_devclass }|.
|
|
33
|
+
ELSE.
|
|
34
|
+
rs_info-not_found = abap_true.
|
|
35
|
+
ENDIF.
|
|
36
|
+
|
|
37
|
+
" Get TTYP details from DD40L
|
|
38
|
+
SELECT SINGLE rowtype accessmode keydef FROM dd40l
|
|
39
|
+
INTO (lv_linetype, lv_tabprottype, lv_keydef)
|
|
40
|
+
WHERE typename = iv_name
|
|
41
|
+
AND as4local = 'A'.
|
|
42
|
+
|
|
43
|
+
" Build components table with TTYP details
|
|
44
|
+
IF lv_linetype IS NOT INITIAL.
|
|
45
|
+
APPEND VALUE #(
|
|
46
|
+
field = 'LINE_TYPE'
|
|
47
|
+
key = abap_false
|
|
48
|
+
type = 'CHAR'
|
|
49
|
+
length = 30
|
|
50
|
+
dataelement = ''
|
|
51
|
+
description = |Line Type: { lv_linetype }|
|
|
52
|
+
) TO rs_info-components.
|
|
53
|
+
ENDIF.
|
|
54
|
+
|
|
55
|
+
" Convert access mode to text
|
|
56
|
+
CASE lv_tabprottype.
|
|
57
|
+
WHEN 'T'. lv_access_mode = 'STANDARD'.
|
|
58
|
+
WHEN 'S'. lv_access_mode = 'SORTED'.
|
|
59
|
+
WHEN 'H'. lv_access_mode = 'HASHED'.
|
|
60
|
+
WHEN OTHERS. lv_access_mode = lv_tabprottype.
|
|
61
|
+
ENDCASE.
|
|
62
|
+
|
|
63
|
+
IF lv_access_mode IS NOT INITIAL.
|
|
64
|
+
APPEND VALUE #(
|
|
65
|
+
field = 'ACCESS_MODE'
|
|
66
|
+
key = abap_false
|
|
67
|
+
type = 'CHAR'
|
|
68
|
+
length = 10
|
|
69
|
+
dataelement = ''
|
|
70
|
+
description = |Access Mode: { lv_access_mode }|
|
|
71
|
+
) TO rs_info-components.
|
|
72
|
+
ENDIF.
|
|
73
|
+
|
|
74
|
+
" Convert key definition to text
|
|
75
|
+
CASE lv_keydef.
|
|
76
|
+
WHEN 'D'. lv_key_definition = 'WITH KEY'.
|
|
77
|
+
WHEN 'N'. lv_key_definition = 'NO KEY'.
|
|
78
|
+
WHEN OTHERS. lv_key_definition = lv_keydef.
|
|
79
|
+
ENDCASE.
|
|
80
|
+
|
|
81
|
+
IF lv_key_definition IS NOT INITIAL.
|
|
82
|
+
APPEND VALUE #(
|
|
83
|
+
field = 'KEY_DEFINITION'
|
|
84
|
+
key = abap_false
|
|
85
|
+
type = 'CHAR'
|
|
86
|
+
length = 10
|
|
87
|
+
dataelement = ''
|
|
88
|
+
description = |Key Definition: { lv_key_definition }|
|
|
89
|
+
) TO rs_info-components.
|
|
90
|
+
ENDIF.
|
|
91
|
+
ENDMETHOD.
|
|
92
|
+
|
|
93
|
+
ENDCLASS.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
|
|
3
|
+
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
|
|
4
|
+
<asx:values>
|
|
5
|
+
<VSEOCLASS>
|
|
6
|
+
<CLSNAME>ZCL_ABGAGT_VIEWER_TTYP</CLSNAME>
|
|
7
|
+
<LANGU>E</LANGU>
|
|
8
|
+
<DESCRIPT>Viewer for ABAP Table Types</DESCRIPT>
|
|
9
|
+
<EXPOSURE>2</EXPOSURE>
|
|
10
|
+
<STATE>1</STATE>
|
|
11
|
+
<UNICODE>X</UNICODE>
|
|
12
|
+
</VSEOCLASS>
|
|
13
|
+
</asx:values>
|
|
14
|
+
</asx:abap>
|
|
15
|
+
</abapGit>
|
|
@@ -6,6 +6,7 @@ INTERFACE zif_abgagt_viewer PUBLIC.
|
|
|
6
6
|
" @parameter rs_info | Object information structure
|
|
7
7
|
METHODS get_info
|
|
8
8
|
IMPORTING iv_name TYPE string
|
|
9
|
-
RETURNING VALUE(rs_info) TYPE zcl_abgagt_command_view=>ty_view_object
|
|
9
|
+
RETURNING VALUE(rs_info) TYPE zcl_abgagt_command_view=>ty_view_object
|
|
10
|
+
RAISING cx_dd_ddl_read.
|
|
10
11
|
|
|
11
12
|
ENDINTERFACE.
|
package/bin/abapgit-agent
CHANGED
|
@@ -309,33 +309,70 @@ async function syntaxCheckSource(sourceFile, csrfToken, config) {
|
|
|
309
309
|
|
|
310
310
|
const result = await request('POST', '/sap/bc/z_abapgit_agent/inspect', data, { csrfToken: csrfToken });
|
|
311
311
|
|
|
312
|
-
// Handle
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
// Handle new table-based result format (array) or old single result
|
|
313
|
+
let results = [];
|
|
314
|
+
if (Array.isArray(result)) {
|
|
315
|
+
results = result;
|
|
316
|
+
} else {
|
|
317
|
+
// Convert old single result to array format for compatibility
|
|
318
|
+
results = [{
|
|
319
|
+
OBJECT_TYPE: 'UNKNOWN',
|
|
320
|
+
OBJECT_NAME: pathModule.basename(sourceFile),
|
|
321
|
+
SUCCESS: result.SUCCESS !== undefined ? result.SUCCESS === 'X' || result.SUCCESS === true : result.success,
|
|
322
|
+
ERROR_COUNT: result.ERROR_COUNT || result.error_count || 0,
|
|
323
|
+
ERRORS: result.ERRORS || result.errors || [],
|
|
324
|
+
WARNINGS: result.warnings || []
|
|
325
|
+
}];
|
|
326
|
+
}
|
|
316
327
|
|
|
317
|
-
|
|
328
|
+
// Process each result
|
|
329
|
+
let hasErrors = false;
|
|
330
|
+
for (const res of results) {
|
|
331
|
+
// Handle both uppercase and lowercase keys
|
|
332
|
+
const success = res.SUCCESS !== undefined ? res.SUCCESS : res.success;
|
|
333
|
+
const objectType = res.OBJECT_TYPE !== undefined ? res.OBJECT_TYPE : res.object_type;
|
|
334
|
+
const objectName = res.OBJECT_NAME !== undefined ? res.OBJECT_NAME : res.object_name;
|
|
335
|
+
const errorCount = res.ERROR_COUNT !== undefined ? res.ERROR_COUNT : (res.error_count || 0);
|
|
336
|
+
const errors = res.ERRORS !== undefined ? res.ERRORS : (res.errors || []);
|
|
337
|
+
const warnings = res.WARNINGS !== undefined ? res.WARNINGS : (res.warnings || []);
|
|
338
|
+
|
|
339
|
+
console.log('\n');
|
|
340
|
+
|
|
341
|
+
if (errorCount > 0 || warnings.length > 0) {
|
|
342
|
+
if (errorCount > 0) {
|
|
343
|
+
console.log(`❌ ${objectType} ${objectName} - Syntax check failed (${errorCount} error(s)):`);
|
|
344
|
+
} else {
|
|
345
|
+
console.log(`⚠️ ${objectType} ${objectName} - Syntax check passed with warnings (${warnings.length}):`);
|
|
346
|
+
}
|
|
347
|
+
console.log('\nErrors:');
|
|
348
|
+
console.log('─'.repeat(60));
|
|
318
349
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
350
|
+
for (const err of errors) {
|
|
351
|
+
const line = err.LINE || err.line || '?';
|
|
352
|
+
const column = err.COLUMN || err.column || '?';
|
|
353
|
+
const text = err.TEXT || err.text || 'Unknown error';
|
|
323
354
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
355
|
+
console.log(` Line ${line}, Column ${column}:`);
|
|
356
|
+
console.log(` ${text}`);
|
|
357
|
+
console.log('');
|
|
358
|
+
}
|
|
328
359
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
360
|
+
// Show warnings if any
|
|
361
|
+
if (warnings.length > 0) {
|
|
362
|
+
console.log('Warnings:');
|
|
363
|
+
console.log('─'.repeat(60));
|
|
364
|
+
for (const warn of warnings) {
|
|
365
|
+
const line = warn.LINE || warn.line || '?';
|
|
366
|
+
const text = warn.MESSAGE || warn.message || 'Unknown warning';
|
|
367
|
+
console.log(` Line ${line}: ${text}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
hasErrors = true;
|
|
371
|
+
} else if (success === true || success === 'X') {
|
|
372
|
+
console.log(`✅ ${objectType} ${objectName} - Syntax check passed`);
|
|
373
|
+
} else {
|
|
374
|
+
console.log(`⚠️ ${objectType} ${objectName} - Syntax check returned unexpected status`);
|
|
332
375
|
}
|
|
333
|
-
} else if (success === 'X' || success === true) {
|
|
334
|
-
console.log(`✅ ${pathModule.basename(sourceFile)} - Syntax check passed (0 errors)`);
|
|
335
|
-
} else if (success === false) {
|
|
336
|
-
console.log(`❌ ${pathModule.basename(sourceFile)} - Syntax check failed`);
|
|
337
|
-
} else {
|
|
338
|
-
console.log(`⚠️ Syntax check returned unexpected status: ${success}`);
|
|
339
376
|
}
|
|
340
377
|
|
|
341
378
|
return result;
|
|
@@ -345,6 +382,95 @@ async function syntaxCheckSource(sourceFile, csrfToken, config) {
|
|
|
345
382
|
}
|
|
346
383
|
}
|
|
347
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Inspect all files in one request
|
|
387
|
+
*/
|
|
388
|
+
async function inspectAllFiles(files, csrfToken, config) {
|
|
389
|
+
// Convert files to uppercase names (same as syntaxCheckSource does)
|
|
390
|
+
const fileNames = files.map(f => {
|
|
391
|
+
const baseName = pathModule.basename(f).toUpperCase();
|
|
392
|
+
return baseName;
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
// Send all files in one request
|
|
397
|
+
const data = {
|
|
398
|
+
files: fileNames
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const result = await request('POST', '/sap/bc/z_abapgit_agent/inspect', data, { csrfToken: csrfToken });
|
|
402
|
+
|
|
403
|
+
// Handle both table result and old single result
|
|
404
|
+
let results = [];
|
|
405
|
+
if (Array.isArray(result)) {
|
|
406
|
+
results = result;
|
|
407
|
+
} else {
|
|
408
|
+
// Convert single result to array format
|
|
409
|
+
results = [{
|
|
410
|
+
OBJECT_TYPE: 'UNKNOWN',
|
|
411
|
+
OBJECT_NAME: files.join(', '),
|
|
412
|
+
SUCCESS: result.SUCCESS !== undefined ? result.SUCCESS === 'X' || result.SUCCESS === true : result.success,
|
|
413
|
+
ERROR_COUNT: result.ERROR_COUNT || result.error_count || 0,
|
|
414
|
+
ERRORS: result.ERRORS || result.errors || [],
|
|
415
|
+
WARNINGS: result.warnings || []
|
|
416
|
+
}];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return results;
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error(`\n Error: ${error.message}`);
|
|
422
|
+
process.exit(1);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Process a single inspect result
|
|
428
|
+
*/
|
|
429
|
+
async function processInspectResult(res) {
|
|
430
|
+
// Handle both uppercase and lowercase keys
|
|
431
|
+
const success = res.SUCCESS !== undefined ? res.SUCCESS : res.success;
|
|
432
|
+
const objectType = res.OBJECT_TYPE !== undefined ? res.OBJECT_TYPE : res.object_type;
|
|
433
|
+
const objectName = res.OBJECT_NAME !== undefined ? res.OBJECT_NAME : res.object_name;
|
|
434
|
+
const errorCount = res.ERROR_COUNT !== undefined ? res.ERROR_COUNT : (res.error_count || 0);
|
|
435
|
+
const errors = res.ERRORS !== undefined ? res.ERRORS : (res.errors || []);
|
|
436
|
+
const warnings = res.WARNINGS !== undefined ? res.WARNINGS : (res.warnings || []);
|
|
437
|
+
|
|
438
|
+
if (errorCount > 0 || warnings.length > 0) {
|
|
439
|
+
if (errorCount > 0) {
|
|
440
|
+
console.log(`❌ ${objectType} ${objectName} - Syntax check failed (${errorCount} error(s)):`);
|
|
441
|
+
} else {
|
|
442
|
+
console.log(`⚠️ ${objectType} ${objectName} - Syntax check passed with warnings (${warnings.length}):`);
|
|
443
|
+
}
|
|
444
|
+
console.log('\nErrors:');
|
|
445
|
+
console.log('─'.repeat(60));
|
|
446
|
+
|
|
447
|
+
for (const err of errors) {
|
|
448
|
+
const line = err.LINE || err.line || '?';
|
|
449
|
+
const column = err.COLUMN || err.column || '?';
|
|
450
|
+
const text = err.TEXT || err.text || 'Unknown error';
|
|
451
|
+
|
|
452
|
+
console.log(` Line ${line}, Column ${column}:`);
|
|
453
|
+
console.log(` ${text}`);
|
|
454
|
+
console.log('');
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Show warnings if any
|
|
458
|
+
if (warnings.length > 0) {
|
|
459
|
+
console.log('Warnings:');
|
|
460
|
+
console.log('─'.repeat(60));
|
|
461
|
+
for (const warn of warnings) {
|
|
462
|
+
const line = warn.LINE || warn.line || '?';
|
|
463
|
+
const text = warn.MESSAGE || warn.message || 'Unknown warning';
|
|
464
|
+
console.log(` Line ${line}: ${text}`);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} else if (success === true || success === 'X') {
|
|
468
|
+
console.log(`✅ ${objectType} ${objectName} - Syntax check passed`);
|
|
469
|
+
} else {
|
|
470
|
+
console.log(`⚠️ ${objectType} ${objectName} - Syntax check returned unexpected status`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
348
474
|
/**
|
|
349
475
|
* Run unit tests for package or objects
|
|
350
476
|
*/
|
|
@@ -691,7 +817,46 @@ async function pull(gitUrl, branch = 'main', files = null, transportRequest = nu
|
|
|
691
817
|
'W': '⚠️', // Warning
|
|
692
818
|
'A': '🛑' // Abort
|
|
693
819
|
};
|
|
694
|
-
return icons[type] || '
|
|
820
|
+
return icons[type] || '';
|
|
821
|
+
};
|
|
822
|
+
|
|
823
|
+
// Calculate display width accounting for emoji (2 cells) vs ASCII (1 cell)
|
|
824
|
+
const calcWidth = (str) => {
|
|
825
|
+
if (!str) return 0;
|
|
826
|
+
let width = 0;
|
|
827
|
+
let i = 0;
|
|
828
|
+
while (i < str.length) {
|
|
829
|
+
const code = str.codePointAt(i);
|
|
830
|
+
if (!code) break;
|
|
831
|
+
// Variation selectors (FE00-FE0F) and ZWJ (200D) take 0 width
|
|
832
|
+
if (code >= 0xFE00 && code <= 0xFE0F) {
|
|
833
|
+
i += 1;
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
if (code === 0x200D) { // Zero width joiner
|
|
837
|
+
i += 1;
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
// Emoji and wide characters take 2 cells
|
|
841
|
+
if (code > 0xFFFF) {
|
|
842
|
+
width += 2;
|
|
843
|
+
i += 2; // Skip surrogate pair
|
|
844
|
+
} else if (code > 127) {
|
|
845
|
+
width += 2;
|
|
846
|
+
i += 1;
|
|
847
|
+
} else {
|
|
848
|
+
width += 1;
|
|
849
|
+
i += 1;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return width;
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
// Pad string to display width
|
|
856
|
+
const padToWidth = (str, width) => {
|
|
857
|
+
const s = str || '';
|
|
858
|
+
const currentWidth = calcWidth(s);
|
|
859
|
+
return s + ' '.repeat(Math.max(0, width - currentWidth));
|
|
695
860
|
};
|
|
696
861
|
|
|
697
862
|
if (success === 'X' || success === true) {
|
|
@@ -719,23 +884,15 @@ async function pull(gitUrl, branch = 'main', files = null, transportRequest = nu
|
|
|
719
884
|
|
|
720
885
|
// Calculate column widths based on terminal width
|
|
721
886
|
const tableWidth = Math.min(TERM_WIDTH, 120);
|
|
722
|
-
const iconCol =
|
|
887
|
+
const iconCol = 4; // Fixed width for icon column
|
|
723
888
|
const objCol = 28;
|
|
724
889
|
const msgCol = tableWidth - iconCol - objCol - 6; // Account for vertical lines (3 chars)
|
|
725
890
|
|
|
726
|
-
//
|
|
727
|
-
const getVisibleWidth = (str) => {
|
|
728
|
-
let width = 0;
|
|
729
|
-
for (const char of str) {
|
|
730
|
-
width += (char.charCodeAt(0) > 127) ? 2 : 1; // Emoji/wide chars = 2
|
|
731
|
-
}
|
|
732
|
-
return width;
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
// Helper to pad to visible width
|
|
891
|
+
// Pad string to display width using calcWidth for emoji support
|
|
736
892
|
const padToWidth = (str, width) => {
|
|
737
|
-
const
|
|
738
|
-
|
|
893
|
+
const s = str || '';
|
|
894
|
+
const currentWidth = calcWidth(s);
|
|
895
|
+
return s + ' '.repeat(Math.max(0, width - currentWidth));
|
|
739
896
|
};
|
|
740
897
|
|
|
741
898
|
const headerLine = '─'.repeat(iconCol) + '┼' + '─'.repeat(objCol) + '┼' + '─'.repeat(msgCol);
|
|
@@ -1293,8 +1450,12 @@ Examples:
|
|
|
1293
1450
|
const config = loadConfig();
|
|
1294
1451
|
const csrfToken = await fetchCsrfToken(config);
|
|
1295
1452
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1453
|
+
// Send all files in one request
|
|
1454
|
+
const results = await inspectAllFiles(filesSyntaxCheck, csrfToken, config);
|
|
1455
|
+
|
|
1456
|
+
// Process results
|
|
1457
|
+
for (const result of results) {
|
|
1458
|
+
await processInspectResult(result);
|
|
1298
1459
|
}
|
|
1299
1460
|
break;
|
|
1300
1461
|
}
|
|
@@ -1430,9 +1591,9 @@ Examples:
|
|
|
1430
1591
|
console.log(` ${description}`);
|
|
1431
1592
|
}
|
|
1432
1593
|
|
|
1433
|
-
// Display source code for classes and
|
|
1594
|
+
// Display source code for classes, interfaces, and CDS views
|
|
1434
1595
|
const source = obj.SOURCE || obj.source || '';
|
|
1435
|
-
if (source && (objType === 'INTF' || objType === 'Interface' || objType === 'CLAS' || objType === 'Class')) {
|
|
1596
|
+
if (source && (objType === 'INTF' || objType === 'Interface' || objType === 'CLAS' || objType === 'Class' || objType === 'DDLS' || objType === 'CDS View')) {
|
|
1436
1597
|
console.log('');
|
|
1437
1598
|
// Replace escaped newlines with actual newlines and display
|
|
1438
1599
|
const displaySource = source.replace(/\\n/g, '\n');
|
|
@@ -1500,6 +1661,15 @@ Examples:
|
|
|
1500
1661
|
}
|
|
1501
1662
|
|
|
1502
1663
|
console.log(end);
|
|
1664
|
+
} else if (objType === 'TTYP' || objType === 'Table Type') {
|
|
1665
|
+
// Show TTYP details as simple text lines
|
|
1666
|
+
console.log('');
|
|
1667
|
+
for (const comp of components) {
|
|
1668
|
+
const desc = comp.DESCRIPTION || comp.description || '';
|
|
1669
|
+
if (desc) {
|
|
1670
|
+
console.log(` ${desc}`);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1503
1673
|
} else {
|
|
1504
1674
|
// Build table display for TABL/STRU with Data Element and Description
|
|
1505
1675
|
const colWidths = {
|
package/docs/view-command.md
CHANGED
|
@@ -28,6 +28,8 @@ abapgit-agent view --objects ZIF_MY_INT --type INTF
|
|
|
28
28
|
abapgit-agent view --objects ZMY_STRUCT --type STRU
|
|
29
29
|
abapgit-agent view --objects ZMY_TABLE --type TABL
|
|
30
30
|
abapgit-agent view --objects ZMY_DTEL --type DTEL
|
|
31
|
+
abapgit-agent view --objects ZMY_TTYP --type TTYP
|
|
32
|
+
abapgit-agent view --objects ZC_MY_CDS_VIEW --type DDLS
|
|
31
33
|
|
|
32
34
|
# View multiple objects
|
|
33
35
|
abapgit-agent view --objects ZCL_CLASS1,ZCL_CLASS2,ZIF_INTERFACE1
|
|
@@ -49,7 +51,7 @@ abapgit-agent view --objects ZCL_MY_CLASS --json
|
|
|
49
51
|
| Parameter | Required | Description |
|
|
50
52
|
|-----------|----------|-------------|
|
|
51
53
|
| `--objects` | Yes | Comma-separated list of object names (e.g., `ZCL_MY_CLASS,ZIF_MY_INTERFACE`) |
|
|
52
|
-
| `--type` | No | Object type for all objects (CLAS, INTF, TABL, STRU, DTEL). Auto-detected from TADIR if not specified |
|
|
54
|
+
| `--type` | No | Object type for all objects (CLAS, INTF, TABL, STRU, DTEL, TTYP, DDLS). Auto-detected from TADIR if not specified |
|
|
53
55
|
| `--json` | No | Output raw JSON only (for scripting) |
|
|
54
56
|
|
|
55
57
|
---
|
|
@@ -185,6 +187,36 @@ DATA ELEMENT S_CARR_ID:
|
|
|
185
187
|
└────────────────────┴──────────────────────────────────────────┘
|
|
186
188
|
```
|
|
187
189
|
|
|
190
|
+
### Table Type Definition (TTYP)
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
📖 DDL2DDICWARNINGS (Table Type)
|
|
194
|
+
Table Type DDL2DDICWARNINGS in SDDL_BASIC_FUNCTIONS
|
|
195
|
+
|
|
196
|
+
Line Type: DDL2DDICERR
|
|
197
|
+
Access Mode: STANDARD
|
|
198
|
+
Key Definition: WITH KEY
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### CDS View Definition (DDLS)
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
📖 ZC_MY_CDS_VIEW (CDS View)
|
|
205
|
+
CDS View ZC_MY_CDS_VIEW in $PACKAGE
|
|
206
|
+
|
|
207
|
+
@AbapCatalog.sqlViewName: 'ZCMYVIEW'
|
|
208
|
+
@AbapCatalog.compiler.compareFilter: true
|
|
209
|
+
@AccessControl.authorizationCheck: #NOT_REQUIRED
|
|
210
|
+
@EndUserText.label: 'My CDS View'
|
|
211
|
+
define view ZC_MY_CDS_VIEW as select from tdevc
|
|
212
|
+
{
|
|
213
|
+
key devclass as Devclass,
|
|
214
|
+
parentcl as ParentPackage,
|
|
215
|
+
ctext as Description
|
|
216
|
+
}
|
|
217
|
+
where devclass not like '$%'
|
|
218
|
+
```
|
|
219
|
+
|
|
188
220
|
### Multiple Objects
|
|
189
221
|
|
|
190
222
|
```
|
|
@@ -240,7 +272,7 @@ DATA ELEMENT S_CARR_ID:
|
|
|
240
272
|
"OBJECTS": [
|
|
241
273
|
{
|
|
242
274
|
"NAME": "string",
|
|
243
|
-
"TYPE": "CLAS|INTF|TABL|STRU|DTEL",
|
|
275
|
+
"TYPE": "CLAS|INTF|TABL|STRU|DTEL|TTYP|DDLS",
|
|
244
276
|
"TYPE_TEXT": "string",
|
|
245
277
|
"DESCRIPTION": "string",
|
|
246
278
|
"DOMAIN": "string", // For DTEL
|
|
@@ -307,6 +339,8 @@ DATA ELEMENT S_CARR_ID:
|
|
|
307
339
|
| `TABL` | Table | Database table |
|
|
308
340
|
| `STRU` | Structure | Structure type |
|
|
309
341
|
| `DTEL` | Data Element | Data element/domain type |
|
|
342
|
+
| `TTYP` | Table Type | Table type definition |
|
|
343
|
+
| `DDLS` | CDS View | CDS View/Entity definition |
|
|
310
344
|
|
|
311
345
|
---
|
|
312
346
|
|
|
@@ -325,6 +359,9 @@ abapgit-agent view --objects SFLIGHT --type TABL
|
|
|
325
359
|
# View data element
|
|
326
360
|
abapgit-agent view --objects S_CARR_ID --type DTEL
|
|
327
361
|
|
|
362
|
+
# View CDS view definition
|
|
363
|
+
abapgit-agent view --objects ZC_MY_CDS_VIEW --type DDLS
|
|
364
|
+
|
|
328
365
|
# View multiple objects
|
|
329
366
|
abapgit-agent view --objects ZCL_CONFIG,ZIF_LOGGER,ZCL_UTILS
|
|
330
367
|
|
|
@@ -350,6 +387,7 @@ abapgit-agent view --objects sflight --type tabl
|
|
|
350
387
|
| **DD02L** | Table/structure definitions |
|
|
351
388
|
| **DD03L** | Table/structure fields |
|
|
352
389
|
| **DD04L** | Data element definitions |
|
|
390
|
+
| **DD40L** | Table type definitions |
|
|
353
391
|
|
|
354
392
|
### Class Source Retrieval (CLAS)
|
|
355
393
|
|
|
@@ -407,3 +445,57 @@ SELECT SINGLE rollname, ddtext, datatype, leng, decimals
|
|
|
407
445
|
INTO (lv_domain, lv_desc, lv_type, lv_len, lv_decimals)
|
|
408
446
|
WHERE rollname = iv_name.
|
|
409
447
|
```
|
|
448
|
+
|
|
449
|
+
### Table Type Retrieval (TTYP)
|
|
450
|
+
|
|
451
|
+
```abap
|
|
452
|
+
" Get TTYP details from DD40L
|
|
453
|
+
SELECT SINGLE rowtype accessmode keydef FROM dd40l
|
|
454
|
+
INTO (lv_linetype, lv_tabprottype, lv_keydef)
|
|
455
|
+
WHERE typename = iv_name
|
|
456
|
+
AND as4local = 'A'.
|
|
457
|
+
|
|
458
|
+
" Convert codes to text:
|
|
459
|
+
" - Access mode: T=STANDARD, S=SORTED, H=HASHED
|
|
460
|
+
" - Key definition: D=WITH KEY, N=NO KEY
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### CDS View Retrieval (DDLS)
|
|
464
|
+
|
|
465
|
+
```abap
|
|
466
|
+
" Use DDL handler to read CDS view source
|
|
467
|
+
lo_handler = cl_dd_ddl_handler_factory=>create( ).
|
|
468
|
+
|
|
469
|
+
" First try to read inactive version (get_state = 'M')
|
|
470
|
+
TRY.
|
|
471
|
+
lo_handler->read(
|
|
472
|
+
EXPORTING
|
|
473
|
+
name = lv_ddls_name
|
|
474
|
+
get_state = 'M'
|
|
475
|
+
IMPORTING
|
|
476
|
+
ddddlsrcv_wa = ls_ddlsrcv ).
|
|
477
|
+
|
|
478
|
+
IF ls_ddlsrcv-source IS NOT INITIAL.
|
|
479
|
+
lv_found = abap_true.
|
|
480
|
+
ENDIF.
|
|
481
|
+
|
|
482
|
+
CATCH cx_dd_ddl_check.
|
|
483
|
+
" Ignore - will try active version
|
|
484
|
+
ENDTRY.
|
|
485
|
+
|
|
486
|
+
" If no inactive version, try active version
|
|
487
|
+
IF lv_found = abap_false.
|
|
488
|
+
TRY.
|
|
489
|
+
lo_handler->read(
|
|
490
|
+
EXPORTING
|
|
491
|
+
name = lv_ddls_name
|
|
492
|
+
get_state = 'A'
|
|
493
|
+
IMPORTING
|
|
494
|
+
ddddlsrcv_wa = ls_ddlsrcv ).
|
|
495
|
+
CATCH cx_dd_ddl_check.
|
|
496
|
+
" Not found
|
|
497
|
+
ENDTRY.
|
|
498
|
+
ENDIF.
|
|
499
|
+
|
|
500
|
+
" Source code is in ls_ddlsrcv-source
|
|
501
|
+
```
|