Skip to content

Commit 7dd8900

Browse files
committed
refactoring and as-atelier-export
1 parent 12205ae commit 7dd8900

11 files changed

+522
-302
lines changed

commands/compile.js

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
const { workspace, window } = require('vscode')
2+
3+
let api = {
4+
putDoc: () => {},
5+
compile: ( ) => {}
6+
}
7+
8+
let log = msg => console.log( 'cos.compile:', msg )
9+
10+
// read from package.json, see below
11+
let languages = [ 'cacheobjectscript', 'cacheobjectscriptinclude' ];
12+
13+
const consoleOutput = ( output, defaultOutput = '' ) => {
14+
15+
const isArr = output instanceof Array
16+
let out = isArr ? output.join( '\n' )
17+
: ( output || defaultOutput ) + ''
18+
;
19+
20+
out = out.replace( /^[\s\r\n]+/, '' )
21+
22+
if ( out ) {
23+
log( out )
24+
}
25+
26+
}
27+
28+
29+
const checkEditor = () => {
30+
31+
if ( !window.activeTextEditor ){
32+
log( 'No active editor, open one at first' )
33+
return false
34+
}
35+
36+
const openedDoc = window.activeTextEditor.document
37+
if ( !openedDoc ){
38+
log( 'Open a Caché ObjectScript file first.' )
39+
return false
40+
}
41+
42+
const fullname = openedDoc.fileName
43+
if ( !fullname ){
44+
log( 'You must save the document first' )
45+
return false
46+
}
47+
48+
const language = openedDoc.languageId
49+
const supported = ~languages.indexOf( language )
50+
if ( !supported ){
51+
log(
52+
`Document ${ fullname } cannot be compiled in Caché
53+
( type ${ language } unsupported )`
54+
)
55+
return false
56+
}
57+
58+
return true
59+
60+
}
61+
62+
63+
// is name correlate with code ?
64+
// if ( atelier ) {
65+
// testClass({ codename: 'test.class.cls', filename: 'class.cls' })
66+
// } else {
67+
// testClass({ codename: 'test.class.cls', filename: 'test.class.cls' })
68+
//}
69+
const testClass = ( { codename, filename }) => {
70+
71+
const parts = codename.split( /\./g )
72+
.filter( s => !!s ) // for codename '.cls'
73+
74+
if ( parts.length < 3 ) { // 'cls' or empty
75+
log(
76+
`Unable to detect class name in source code of ${ filename }.\n
77+
Is it a valid Caché ObjectScript class?`
78+
)
79+
return false
80+
}
81+
82+
// NOTE: by default used package 'User'
83+
// else if ( parts.length === 2 ){
84+
// parts.unshift( 'User' ) //package by default
85+
// or need detect 'import' directive
86+
//}
87+
88+
const codenameL = codename.toLowerCase()
89+
const filenameL = filename.toLowerCase()
90+
const isContain = ~codenameL.indexOf( filenameL )
91+
92+
if ( !isContain ){
93+
94+
log(
95+
`You tried to compile class named '${ docname }' in file '${ filename }'.\n
96+
Did you forget to rename the file/class to correspond to each other?`
97+
)
98+
return false
99+
100+
}
101+
return true
102+
103+
}
104+
105+
106+
const testRoutine = ({ codename, filename, type }) => {
107+
108+
if ( !codename ){
109+
110+
log(
111+
112+
`Unable to detect routine name in source code of ${ filename }.
113+
Is it a valid Caché ObjectScript routine? Did you forget to define a routine
114+
name in the file on the first line? Routine code example:
115+
116+
ROUTINE RtnName [Type=MAC]
117+
write "routine code here"
118+
Quit
119+
`
120+
)
121+
return false
122+
123+
}
124+
125+
const isContain = ~codename.toLowerCase().indexOf( filename.toLowerCase() )
126+
if ( !isContain ) {
127+
log(
128+
`You tried to compile routine named "${ cacheDocName }" (.${ rtnType }) in file "${
129+
matchingFileName }".\nDid you forget to rename the file/routine to correspond to each other? `
130+
+ `Routine code example: \n\n`
131+
+ `ROUTINE ${ matchingName } [Type=${ rtnType }]`
132+
+ `\n write "routine code here"\n quit`
133+
)
134+
return false
135+
}
136+
137+
return true
138+
139+
140+
}
141+
142+
const AnyErrors = codename => ( err, res, keyword ) => {
143+
144+
if ( err ){
145+
const errtext = err.code ? err.code + ' ' + err.message : err
146+
log( `Unable to ${ keyword } ${ codename }: ${ errtext }` )
147+
return true
148+
}
149+
150+
151+
if ( !res || !res.status || !( res.status.errors instanceof Array ) ){
152+
log( `Unknown response from Atelier API while trying to ${
153+
keyword } ${ codename }: ${ res }` )
154+
return true
155+
}
156+
157+
if ( res.result && res.result.status ){
158+
log( res.result.status )
159+
return true
160+
}
161+
162+
if ( res.status.errors.length !== 0 ){
163+
log(
164+
`Unable to ${ keyword } ${ codename }: ${ res.status.errors.summary }
165+
${res.console }\n\n${ res.status.errors.join('\n') }`
166+
)
167+
return true
168+
}
169+
170+
return false;
171+
172+
}
173+
174+
175+
176+
/**
177+
* Import and compile current file.
178+
*/
179+
const cosCompile = () => {
180+
181+
if ( !checkEditor() ) return
182+
183+
const activedoc = window.activeTextEditor.document
184+
const fullname = activedoc.fileName
185+
log( `Saving ${ fullname }...` )
186+
187+
//drop folders, all after last '/'
188+
let filename = ( fullname.match( /[^\\\/]+$/ ) || [] )[ 0 ] || ''
189+
190+
let code = activedoc.getText()
191+
.replace( /\/\/[^\r\n]*\r?\n/g, '' ) //normalize eol?
192+
193+
let codename; //server side name
194+
195+
const isClass = /\.cls$/i.test( fullname )
196+
if ( isClass ) {
197+
198+
// Caché class files can be placed hierarchically (e.g. /src/Package/Class.cls),
199+
// so we pick the class name from the class definition itself
200+
const clsrgx = code.match( /Class ([^\s]+)/i ) // rgx = ['Class test.class', 'test.class']
201+
codename = ( clsrgx || [] )[ 1 ] || ''
202+
codename += '.cls' // test.class.cls
203+
204+
// is name correlate with code ?
205+
// if ( atelier_way ) {
206+
// testClass({ codename: 'test.class.cls', filename: 'class.cls' })
207+
// } else {
208+
// testClass({ codename: 'test.class.cls', filename: 'test.class.cls' })
209+
//}
210+
if ( !testClass( { codename, filename } ) ) return
211+
212+
} else { // routine cases
213+
214+
// routine: routine name must be declared in a routine
215+
const rtnrgx = code.match( /routine ([^\s]+)/i )
216+
codename = ( rtnrgx || [] )[ 1 ] || ''
217+
const type = ( code.match( /routine\s+[^\s]+\s+\[.*type=([a-z]{3,})/i ) || [] )[ 1 ] || 'MAC'
218+
codename += '.' + type
219+
if ( !testRoutine( { codename, filename, type } ) ) return
220+
221+
}
222+
223+
const content = code.split( /\r?\n/g ) // code lines
224+
const anyErrors = AnyErrors( codename )
225+
226+
const onCompile = ( err, res ) => {
227+
if ( anyErrors( err, res, 'compile' ) ) return
228+
consoleOutput( res.console || "Done." )
229+
}
230+
231+
const onSave = ( err, res ) => {
232+
if ( anyErrors( err, res, 'save' ) ) return
233+
consoleOutput( res.console )
234+
api.compile( codename, onCompile )
235+
}
236+
237+
api.putDoc(
238+
codename,
239+
{ enc: false, content },
240+
{ ignoreConflict: true },
241+
onSave
242+
)
243+
244+
}
245+
246+
module.exports = env => {
247+
248+
({ log, api, languages } = env );
249+
return cosCompile
250+
251+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//for example: 'mypkg.subpkg.myclass.cls'
2+
// return 'mypkg/subpkg/'
3+
module.exports = docname => {
4+
5+
const parts = docname.split( '.' ) // [ 'mypkg', 'subpkg', 'myclass', 'cls' ]
6+
const packagesEnd = parts.length - 2 // name and extension
7+
return [
8+
parts.slice( 0, packagesEnd ).join( '/' ), // packages to subfolders
9+
parts.slice( packagesEnd ).join( '.' )
10+
].join('/')
11+
12+
}

commands/export/index.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
4+
//export 'mypkg.subpkg.name.cls' as /mypkg/subpkg/name.cls
5+
const atelier_filename = require('./doc-to-file-as-atelier')
6+
const mkdir = require('./mkdir-p-sync') // mkdir -p 'path/to/file'
7+
8+
// see module.exports
9+
let api = {
10+
getDocNames: ( opts, cb ) => cb( null, {} ),
11+
getDoc: ( docname, cb ) => cb( null, {} )
12+
}
13+
let log = (...msg) => console.log('cos.export:', ...msg )
14+
// export options
15+
let root = '.'
16+
let folder = 'src'
17+
let category = '*'
18+
let generated = 0
19+
let filter = ''
20+
let atelier = false
21+
let doc2file = docname => docname
22+
23+
24+
// Export one document
25+
const docExport = ( doc, cb ) => {
26+
27+
if ( !root ){
28+
log('')
29+
log('Open folder before export - Ctrl+K, Ctrl+O')
30+
return cb()
31+
}
32+
33+
// atelier: 'mypkg.subpkg.myclass.cls' => 'mypkg/subpkg/myclass.cls'
34+
const filename = doc2file( doc.name )
35+
const fullname = [ root, folder, doc.cat, filename ].join('/')
36+
const folders = path.dirname( fullname )
37+
38+
if ( !fs.existsSync( folders ) ) mkdir( folders )
39+
fs.writeFileSync( fullname, doc.content.join( '\n' ) )
40+
41+
log( `${ doc.name } -> ${ fullname }` )
42+
cb( null, {} )
43+
44+
}
45+
46+
const Loaded = cb => ( err, json ) => {
47+
48+
if ( err ) {
49+
log('')
50+
// doc.name ?
51+
log( `ERROR!!!: ${ JSON.stringify( err )}` )
52+
log('')
53+
return cb( err )
54+
}
55+
56+
docExport( json.result, cb )
57+
58+
}
59+
60+
const load = ( doc, cb ) => api.getDoc( encodeURI( doc.name ), Loaded( cb ) )
61+
62+
const docsExport = ( docs, cb ) => {
63+
64+
let doc, loadcb;
65+
while ( doc = docs.shift() ){
66+
67+
loadcb = ( err, data )=> {
68+
if ( err ) log( `ERROR: ${ JSON.stringify( doc ) } ${ JSON.stringify( err ) }` )
69+
}
70+
71+
load( doc, loadcb )
72+
73+
}
74+
75+
}
76+
77+
const onGetDocs = ( err, json ) => {
78+
79+
if ( err ) return log( 'getDocs ERROR' )
80+
81+
const list = json.result.content
82+
log( '' )
83+
log( 'list: ' + list.length )
84+
const docFilter = doc => {
85+
return ( doc.cat !== 'CSP' ) &&
86+
( doc.name.substring( 0, 1 ) !== '%' ) &&
87+
( doc.name.substring( 0, 12 ) !== 'INFORMATION.' )
88+
}
89+
const docs = list.filter( docFilter )
90+
log( 'without % and CSP and INFORMATION: ' + docs.length )
91+
log( '' )
92+
93+
docsExport( docs, () => {
94+
log( '' )
95+
log( 'Export completed.' )
96+
})
97+
98+
}
99+
100+
/**
101+
* Export all classes/routines in a namespace to working directory.
102+
*/
103+
module.exports = environment => {
104+
105+
( { api, log, options } = environment );
106+
( { root, folder, atelier, category, generated, filter } = options );
107+
if ( atelier ) doc2file = docname => atelier_filename( docname )
108+
109+
return () => {
110+
api.getDocNames( { category, generated, filter }, onGetDocs )
111+
}
112+
113+
}

0 commit comments

Comments
 (0)