From afee5dd5f1a60d9085390d3084cae32aea1a8cbe Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 18 Apr 2026 23:35:32 +0530 Subject: [PATCH 1/3] fix: redundant prettier markdown files appears everytime the build runs --- build/api-docs-generator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/api-docs-generator.js b/build/api-docs-generator.js index a5891d8c95..1f391be6b7 100644 --- a/build/api-docs-generator.js +++ b/build/api-docs-generator.js @@ -250,7 +250,7 @@ async function generateMarkdown(file, relativePath) { markdownContent, path.join(relativePath, fileName) ); - await fs.writeFile(tempOutputFileName, updatedMarkdownContent, 'utf-8'); + await fs.writeFile(tempOutputFileName, normalizeLineEndings(updatedMarkdownContent), 'utf-8'); const fileExists = await fs.access(outputFileName).then(() => true).catch( () => false From 2446c81ba0027bc5e604b2071ccb7338b6af4aa6 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sat, 18 Apr 2026 23:56:31 +0530 Subject: [PATCH 2/3] chore: change crlf to lf line endings --- build/api-docs-generator.js | 4 +- docs/API-Reference/NodeConnector.md | 138 +++++++++++++-- .../features/BeautificationManager.md | 118 ++++++++++++- .../features/NewFileContentManager.md | 82 ++++++++- .../features/QuickViewManager.md | 161 +++++++++++++++++- .../features/SelectionViewManager.md | 112 +++++++++++- docs/API-Reference/language/HTMLDOMDiff.md | 19 ++- docs/API-Reference/search/QuickOpenHelper.md | 6 +- docs/API-Reference/search/SearchModel.md | 10 +- docs/API-Reference/view/PluginPanelView.md | 10 +- docs/API-Reference/widgets/PopUpManager.md | 7 +- docs/API-Reference/widgets/StatusBar.md | 5 +- docs/API-Reference/worker/IndexingWorker.md | 65 ++++++- docs/API-Reference/worker/WorkerComm.md | 59 ++++++- 14 files changed, 733 insertions(+), 63 deletions(-) diff --git a/build/api-docs-generator.js b/build/api-docs-generator.js index 1f391be6b7..d1215c93b3 100644 --- a/build/api-docs-generator.js +++ b/build/api-docs-generator.js @@ -209,8 +209,8 @@ function normalizeLineEndings(content) { */ async function areFilesDifferent(file1, file2) { const [content1, content2] = await Promise.all([ - fs.readFile(file1, 'utf-8').then(normalizeLineEndings), - fs.readFile(file2, 'utf-8').then(normalizeLineEndings) + fs.readFile(file1, 'utf-8'), + fs.readFile(file2, 'utf-8') ]); const hash1 = crypto.createHash('md5').update(content1).digest('hex'); diff --git a/docs/API-Reference/NodeConnector.md b/docs/API-Reference/NodeConnector.md index 3f83f2f597..d4dd58f6a0 100644 --- a/docs/API-Reference/NodeConnector.md +++ b/docs/API-Reference/NodeConnector.md @@ -6,26 +6,126 @@ const NodeConnector = brackets.getModule("NodeConnector") ## NodeConnector -Node Connector Communication Module This module simplifies communication between Node.js and Phoenix (phcode). A `NodeConnector` acts as an intermediary, allowing you to execute functions in Node.js from Phoenix and vice versa. You can use the `execPeer` method to call functions on the other side and handle communication seamlessly. Use `triggerPeer` to trigger events on the other side. ## Setting Up a `NodeConnector` To establish communication between two modules, such as `x.js` in Phoenix and `y.js` in Node.js, follow these steps: ### Create `NodeConnector` in Phoenix (`x.js`) +Node Connector Communication Module + +This module simplifies communication between Node.js and Phoenix (phcode). A `NodeConnector` acts as an intermediary, +allowing you to execute functions in Node.js from Phoenix and vice versa. You can use the `execPeer` method to call +functions on the other side and handle communication seamlessly. Use `triggerPeer` to trigger events +on the other side. + +## Setting Up a `NodeConnector` + +To establish communication between two modules, such as `x.js` in Phoenix and `y.js` in Node.js, follow these steps: + +### Create `NodeConnector` in Phoenix (`x.js`) **Example** -```js const NodeConnector = require('NodeConnector'); const XY_NODE_CONNECTOR_ID = 'ext_x_y'; // Use a unique ID let nodeConnector = NodeConnector.createNodeConnector(XY_NODE_CONNECTOR_ID, exports); exports.modifyImage = async function(imageName, imageArrayBuffer) { // Perform image operations with the imageArrayBuffer // To return an ArrayBuffer, return an object with a `buffer` key. return { operationDone: 'colored, cropped', buffer: imageArrayBuffer, }; }; ``` ### Create `NodeConnector` in Node.js (`y.js`) +```js +const NodeConnector = require('NodeConnector'); +const XY_NODE_CONNECTOR_ID = 'ext_x_y'; // Use a unique ID +let nodeConnector = NodeConnector.createNodeConnector(XY_NODE_CONNECTOR_ID, exports); + +exports.modifyImage = async function(imageName, imageArrayBuffer) { + // Perform image operations with the imageArrayBuffer + // To return an ArrayBuffer, return an object with a `buffer` key. + return { + operationDone: 'colored, cropped', + buffer: imageArrayBuffer, + }; +}; +``` + +### Create `NodeConnector` in Node.js (`y.js`) **Example** -```js const XY_NODE_CONNECTOR_ID = 'ext_x_y'; // Use the same unique ID let nodeConnector = global.createNodeConnector(XY_NODE_CONNECTOR_ID, exports); exports.getPWDRelative = async function(subPath) { return process.cwd + '/' + subPath; }; ``` With these steps, a `NodeConnector` is set up, enabling two-way communication. ## Executing Functions To call a Node.js function from Phoenix, use the `execPeer` method. +```js +const XY_NODE_CONNECTOR_ID = 'ext_x_y'; // Use the same unique ID +let nodeConnector = global.createNodeConnector(XY_NODE_CONNECTOR_ID, exports); + +exports.getPWDRelative = async function(subPath) { + return process.cwd + '/' + subPath; +}; +``` + +With these steps, a `NodeConnector` is set up, enabling two-way communication. + +## Executing Functions + +To call a Node.js function from Phoenix, use the `execPeer` method. **Example** -```js // In `x.js` (Phoenix) const fullPath = await nodeConnector.execPeer('getPWDRelative', 'sub/path.html'); ``` To execute a Phoenix function from Node.js and transfer binary data, pass an optional ArrayBuffer. +```js +// In `x.js` (Phoenix) +const fullPath = await nodeConnector.execPeer('getPWDRelative', 'sub/path.html'); +``` + +To execute a Phoenix function from Node.js and transfer binary data, pass an optional ArrayBuffer. **Example** -```js // In `y.js` (Node.js) const { operationDone, buffer } = await nodeConnector.execPeer('modifyImage', {name:'theHills.png'}, imageAsArrayBuffer); ``` ## Event Handling The `NodeConnector` object implements all the APIs supported by `utils/EventDispatcher`. You can trigger and listen to events between Node.js and Phoenix using the `triggerPeer` and (`on`, `one` or `off`) methods. +```js +// In `y.js` (Node.js) +const { operationDone, buffer } = await nodeConnector.execPeer('modifyImage', {name:'theHills.png'}, imageAsArrayBuffer); +``` + +## Event Handling + +The `NodeConnector` object implements all the APIs supported by `utils/EventDispatcher`. You can trigger and listen +to events between Node.js and Phoenix using the `triggerPeer` and (`on`, `one` or `off`) methods. **Example** -```js // In `y.js` (Node.js) nodeConnector.on('phoenixProjectOpened', (_event, projectPath) => { console.log(projectPath); }); nodeConnector.one('phoenixProjectOpened', (_event, projectPath) => { console.log(projectPath + "will be received only once"); }); ``` To raise an event from Phoenix to Node.js: +```js +// In `y.js` (Node.js) +nodeConnector.on('phoenixProjectOpened', (_event, projectPath) => { + console.log(projectPath); +}); + +nodeConnector.one('phoenixProjectOpened', (_event, projectPath) => { + console.log(projectPath + "will be received only once"); +}); +``` + +To raise an event from Phoenix to Node.js: **Example** -```js // In `x.js` (Phoenix) nodeConnector.triggerPeer('phoenixProjectOpened', '/x/project/folder'); ``` To Switch off events +```js +// In `x.js` (Phoenix) +nodeConnector.triggerPeer('phoenixProjectOpened', '/x/project/folder'); +``` + +To Switch off events **Example** -```js nodeConnector.off('phoenixProjectOpened'); // will switch off all event handlers of that name. ``` By Default, all events handlers with the eventName is removed when you call `nodeConnector.off(eventName)` fn. To selectively switch off event handlers, please see reference for `utils/EventDispatcher` module. ### Handling ArrayBuffer Data in Function Execution When executing functions that send or receive binary data, ensure that the functions are asynchronous and accept an optional ArrayBuffer as a parameter. To return binary data, use an object with a `buffer` key. Example of calling a function in Node.js with binary data transfer: +```js +nodeConnector.off('phoenixProjectOpened'); // will switch off all event handlers of that name. +``` + +By Default, all events handlers with the eventName is removed when you call `nodeConnector.off(eventName)` fn. +To selectively switch off event handlers, please see reference for `utils/EventDispatcher` module. + +### Handling ArrayBuffer Data in Function Execution + +When executing functions that send or receive binary data, ensure that the functions are asynchronous and accept an +optional ArrayBuffer as a parameter. To return binary data, use an object with a `buffer` key. + +Example of calling a function in Node.js with binary data transfer: **Example** -```js // In `y.js` (Node.js) const { operationDone, buffer } = await nodeConnector.execPeer('modifyImage', {name:'name.png'}, imageArrayBuffer); ``` ### Handling ArrayBuffer Data in Event Handling Use the `triggerPeer` method to send binary data in events. Include the ArrayBuffer as an optional parameter. Example of sending binary data in an event from Phoenix to Node.js: +```js +// In `y.js` (Node.js) +const { operationDone, buffer } = await nodeConnector.execPeer('modifyImage', {name:'name.png'}, imageArrayBuffer); +``` + +### Handling ArrayBuffer Data in Event Handling + +Use the `triggerPeer` method to send binary data in events. Include the ArrayBuffer as an optional parameter. + +Example of sending binary data in an event from Phoenix to Node.js: **Example** -```js // In `x.js` (Phoenix) const imageArrayBuffer = getSomeImageArrayBuffer(); // Get the ArrayBuffer nodeConnector.triggerPeer('imageEdited', 'name.png', imageArrayBuffer); ``` ## Caveats - Be cautious when sending large binary data, as it may affect performance and memory usage. Transferring large data is fully supported, but be mindful of performance. - Functions called with `execPeer` and `triggerPeer` must be asynchronous and accept a single argument. An optional second argument can be used to transfer large binary data as an ArrayBuffer. For more event handling operations and details, refer to the documentation for the `utils/EventDispatcher` module. +```js +// In `x.js` (Phoenix) +const imageArrayBuffer = getSomeImageArrayBuffer(); // Get the ArrayBuffer +nodeConnector.triggerPeer('imageEdited', 'name.png', imageArrayBuffer); +``` +## Caveats +- Be cautious when sending large binary data, as it may affect performance and memory usage. Transferring large + data is fully supported, but be mindful of performance. +- Functions called with `execPeer` and `triggerPeer` must be asynchronous and accept a single argument. An optional + second argument can be used to transfer large binary data as an ArrayBuffer. + +For more event handling operations and details, refer to the documentation for the `utils/EventDispatcher` module. * [NodeConnector](#module_NodeConnector) * [.createNodeConnector(nodeConnectorID, moduleExports)](#module_NodeConnector..createNodeConnector) ⇒ Object @@ -39,7 +139,23 @@ Node Connector Communication Module This module simplifies communication betwee ### NodeConnector.createNodeConnector(nodeConnectorID, moduleExports) ⇒ Object -Creates a new node connector with the specified ID and module exports. Returns a NodeConnector Object (which is an EventDispatcher with additional `execPeer` and `triggerPeer` methods. `peer` here means, if you are executing `execPeer` in Phoenix, it will execute the named function in node side, and vice versa. You can right away start using `execPeer`, `triggerPeer`(to send/receive events) APIs without waiting to check if the other side nodeConnector is created. Note: If the NodeConnector has not been created on the other end, requests made with `execPeer` or `triggerPeer` will be temporarily queued for up to 10 seconds to allow time for the connector to be created. If the connector is not created within this timeout period, all queued `execPeer` requests will be rejected, and all queued events will be dropped. It is recommended to call the `createNodeConnector` API on both ends within a timeframe of less than 10 seconds(ideally same time) for seamless communication. - execPeer: A function that executes a peer function with specified parameters. - triggerPeer: A function that triggers an event to be sent to a peer. - Also contains all the APIs supported by `utils/EventDispatcher` module. +Creates a new node connector with the specified ID and module exports. + +Returns a NodeConnector Object (which is an EventDispatcher with +additional `execPeer` and `triggerPeer` methods. `peer` here means, if you are executing `execPeer` +in Phoenix, it will execute the named function in node side, and vice versa. You can right away start +using `execPeer`, `triggerPeer`(to send/receive events) APIs without waiting to check if the +other side nodeConnector is created. + +Note: If the NodeConnector has not been created on the other end, requests made with `execPeer` or +`triggerPeer` will be temporarily queued for up to 10 seconds to allow time for the connector to be created. +If the connector is not created within this timeout period, all queued `execPeer` requests will be rejected, +and all queued events will be dropped. It is recommended to call the `createNodeConnector` API on both ends +within a timeframe of less than 10 seconds(ideally same time) for seamless communication. + +- execPeer: A function that executes a peer function with specified parameters. +- triggerPeer: A function that triggers an event to be sent to a peer. +- Also contains all the APIs supported by `utils/EventDispatcher` module. **Kind**: inner method of [NodeConnector](#module_NodeConnector) **Returns**: Object - - A NodeConnector Object. Also contains all the APIs supported by `utils/EventDispatcher` module. diff --git a/docs/API-Reference/features/BeautificationManager.md b/docs/API-Reference/features/BeautificationManager.md index e8706d3706..22fb35bcd2 100644 --- a/docs/API-Reference/features/BeautificationManager.md +++ b/docs/API-Reference/features/BeautificationManager.md @@ -6,18 +6,110 @@ const BeautificationManager = brackets.getModule("features/BeautificationManager ## features/BeautificationManager -Beautification manager interacts with beautify extensions to determine what to do when user issues `beautify code` command. Beautification providers can use this module to register new providers to beautify new languages. ## API ### registerBeautificationProvider Register a Beautification provider with this api. +Beautification manager interacts with beautify extensions to determine what to do when user issues `beautify code` +command. Beautification providers can use this module to register new providers to beautify new languages. + +## API +### registerBeautificationProvider +Register a Beautification provider with this api. **Example** -```js // syntax BeautificationManager.registerBeautificationProvider(provider, supportedLanguages, priority); ``` The API requires three parameters: 1. `provider`: must implement a `beautifyEditorProvider` and `beautifyTextProvider` function. See doc below: 1. `supportedLanguages`: An array of languages that the provider supports. If `["all"]` is supplied, then the provider will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` 1. `priority`: Used to break ties among providers for a particular language. Providers with a higher number will be asked for beatified code before those with a lower priority value. Defaults to zero. +```js +// syntax +BeautificationManager.registerBeautificationProvider(provider, supportedLanguages, priority); +``` +The API requires three parameters: +1. `provider`: must implement a `beautifyEditorProvider` and `beautifyTextProvider` function. See doc below: +1. `supportedLanguages`: An array of languages that the provider supports. If `["all"]` is supplied, then the + provider will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` +1. `priority`: Used to break ties among providers for a particular language. Providers with a higher number + will be asked for beatified code before those with a lower priority value. Defaults to zero. **Example** -```js // to register a provider that will be invoked for all languages. where provider is any object that implements // a `beautifyEditorProvider` and `beautifyTextProvider` function BeautificationManager.registerBeautificationProvider(provider, ["all"]); // to register a provider that will be invoked for specific languages BeautificationManager.registerBeautificationProvider(provider, ["javascript", "html", "php"]); ``` ### removeBeautificationProvider Removes a registered Beautification provider. The API takes the same arguments as `registerBeautificationProvider`. +```js +// to register a provider that will be invoked for all languages. where provider is any object that implements +// a `beautifyEditorProvider` and `beautifyTextProvider` function +BeautificationManager.registerBeautificationProvider(provider, ["all"]); + +// to register a provider that will be invoked for specific languages +BeautificationManager.registerBeautificationProvider(provider, ["javascript", "html", "php"]); +``` + +### removeBeautificationProvider +Removes a registered Beautification provider. The API takes the same arguments as `registerBeautificationProvider`. **Example** -```js // syntax BeautificationManager.removeBeautificationProvider(provider, supportedLanguages); // Example BeautificationManager.removeBeautificationProvider(provider, ["javascript", "html"]); ``` ### provider.beautifyEditorProvider Each provider must implement the `beautifyEditorProvider` function that returns a promise. The promise either resolves with the beautified code details or rejects if there is nothing to beautify for the provider. +```js +// syntax +BeautificationManager.removeBeautificationProvider(provider, supportedLanguages); +// Example +BeautificationManager.removeBeautificationProvider(provider, ["javascript", "html"]); +``` + +### provider.beautifyEditorProvider +Each provider must implement the `beautifyEditorProvider` function that returns a promise. The promise either resolves with +the beautified code details or rejects if there is nothing to beautify for the provider. **Example** -```js // function signature provider.beautifyEditorProvider = function(editor) { return new Promise((resolve, reject)=>{ resolve({ originalText: "the original text sent to beautify", changedText: "partial or full text that changed.", // Optional cursor offset if given will set the editor cursor to the position after beautification. // either `cursorOffset` or `ranges` can be specified, but not both. cursorOffset: number, // Optional: If range is specified, only the given range will be replaced. else full text is replaced ranges:{ replaceStart: {line,ch}, replaceEnd: {line,ch} } }); }); }; ``` #### The resolved promise object The resolved promise should either be `null`(indicating that the extension itself has prettified the code and doesn't want any further processing from BeautificationManager.) or contain the following details: 1. `originalText` - string, the original text sent to beautify 1. `changedText` - string, this should be the fully prettified text of the whole `originalText` or a fragment of pretty text in `originalText` if a range was selected. If a `fragment` is returned, then the `ranges` object must be specified. 1. `cursorOffset` - Optional number, if given will set the editor cursor to the position after beautification. either `cursorOffset` or `ranges` can be specified, but not both. 1. `ranges` - Optional object, set of 2 cursors that gives details on what range to replace with given changed text. If range is not specified, the full text in the editor will be replaced. range has 2 fields: 1. `replaceStart{line,ch}` - the start of range to replace 1. `replaceEnd{line,ch}` - the end of range to replace ### provider.beautifyTextProvider Each provider must implement the `beautifyTextProvider` function that returns a promise. The promise either resolves with the beautified code details(same as beautifyEditorProvider) or rejects if there is nothing to beautify for the provider. +```js +// function signature +provider.beautifyEditorProvider = function(editor) { + return new Promise((resolve, reject)=>{ + resolve({ + originalText: "the original text sent to beautify", + changedText: "partial or full text that changed.", + // Optional cursor offset if given will set the editor cursor to the position after beautification. + // either `cursorOffset` or `ranges` can be specified, but not both. + cursorOffset: number, + // Optional: If range is specified, only the given range will be replaced. else full text is replaced + ranges:{ + replaceStart: {line,ch}, + replaceEnd: {line,ch} + } + }); + }); + }; +``` + +#### The resolved promise object +The resolved promise should either be `null`(indicating that the extension itself has prettified the code and +doesn't want any further processing from BeautificationManager.) or contain the following details: +1. `originalText` - string, the original text sent to beautify +1. `changedText` - string, this should be the fully prettified text of the whole `originalText` or a fragment of + pretty text in `originalText` if a range was selected. If a `fragment` is returned, then the + `ranges` object must be specified. +1. `cursorOffset` - Optional number, if given will set the editor cursor to the position after beautification. + either `cursorOffset` or `ranges` can be specified, but not both. +1. `ranges` - Optional object, set of 2 cursors that gives details on what range to replace with given changed text. + If range is not specified, the full text in the editor will be replaced. range has 2 fields: + 1. `replaceStart{line,ch}` - the start of range to replace + 1. `replaceEnd{line,ch}` - the end of range to replace + +### provider.beautifyTextProvider +Each provider must implement the `beautifyTextProvider` function that returns a promise. +The promise either resolves with the beautified code details(same as beautifyEditorProvider) or rejects if +there is nothing to beautify for the provider. **Example** -```js // function signature. provider.beautifyTextProvider = function(textToBeautify, filePathOrFileName) { return new Promise((resolve, reject)=>{ resolve({ originalText: "the original text sent to beautify", changedText: "partial or full text that changed.", // Optional: If range is specified, only the given range is assumed changed. else full text changed. ranges:{ replaceStart: {line,ch}, replaceEnd: {line,ch} } }); }); }; ``` #### Parameters The `beautifyTextProvider` callback will receive the following arguments. 1. textToBeautify - string 1. filePathOrFileName - string. This will either be a valid file path, or a file name to deduce which language the beautifier is dealing with. #### The resolved promise object The resolved object has the same structure as beautifyEditorProvider resolved promise object. +```js +// function signature. +provider.beautifyTextProvider = function(textToBeautify, filePathOrFileName) { + return new Promise((resolve, reject)=>{ + resolve({ + originalText: "the original text sent to beautify", + changedText: "partial or full text that changed.", + // Optional: If range is specified, only the given range is assumed changed. else full text changed. + ranges:{ + replaceStart: {line,ch}, + replaceEnd: {line,ch} + } + }); + }); + }; +``` +#### Parameters +The `beautifyTextProvider` callback will receive the following arguments. +1. textToBeautify - string +1. filePathOrFileName - string. This will either be a valid file path, or a file name to deduce which language the + beautifier is dealing with. +#### The resolved promise object + The resolved object has the same structure as beautifyEditorProvider resolved promise object. * [features/BeautificationManager](#module_features/BeautificationManager) * [.beautifyEditor(editor)](#module_features/BeautificationManager..beautifyEditor) ⇒ Promise @@ -29,7 +121,8 @@ Beautification manager interacts with beautify extensions to determine what to d Beautifies text in the given editor with available providers. **Kind**: inner method of [features/BeautificationManager](#module_features/BeautificationManager) -**Returns**: Promise - - A promise that will be resolved to null if the selected text is beautified or rejects if beautification failed. +**Returns**: Promise - - A promise that will be resolved to null if the selected text is beautified or rejects +if beautification failed. | Param | | --- | @@ -41,7 +134,16 @@ Beautifies text in the given editor with available providers. Beautifies text with available providers. **Kind**: inner method of [features/BeautificationManager](#module_features/BeautificationManager) -**Returns**: Promise - - A promise that will be resolved to null if the selected text is beautified or rejects if beautification failed.. #### The resolved promise object The resolved promise object contain the following details: 1. `originalText` - string, the original text sent to beautify 1. `changedText` - string, the prettified text. 1. `ranges` - Optional. if range object is returned, it means that only a part of the original text changed in the original text `textToBeautify`. The part that changed is supplied by two cursor positions below: 1. `replaceStart{line,ch}` - the start of range to replace 1. `replaceEnd{line,ch}` - the end of range to replace +**Returns**: Promise - - A promise that will be resolved to null if the selected text is beautified or rejects +if beautification failed.. +#### The resolved promise object +The resolved promise object contain the following details: +1. `originalText` - string, the original text sent to beautify +1. `changedText` - string, the prettified text. +1. `ranges` - Optional. if range object is returned, it means that only a part of the original text changed in + the original text `textToBeautify`. The part that changed is supplied by two cursor positions below: + 1. `replaceStart{line,ch}` - the start of range to replace + 1. `replaceEnd{line,ch}` - the end of range to replace | Param | Type | Description | | --- | --- | --- | diff --git a/docs/API-Reference/features/NewFileContentManager.md b/docs/API-Reference/features/NewFileContentManager.md index de60495b8b..49795c3923 100644 --- a/docs/API-Reference/features/NewFileContentManager.md +++ b/docs/API-Reference/features/NewFileContentManager.md @@ -6,22 +6,90 @@ const NewFileContentManager = brackets.getModule("features/NewFileContentManager ## features/NewFileContentManager -NewFileContentManager provides support to add default template content when a new/empty file is created. Extensions can register to provide content with `NewFileContentManager.registerContentProvider` API. ## Usage Let's say whenever a user creates a new js file, we have to prefill the contents to "sample content" +NewFileContentManager provides support to add default template content when a new/empty file is created. +Extensions can register to provide content with `NewFileContentManager.registerContentProvider` API. + +## Usage +Let's say whenever a user creates a new js file, we have to prefill the contents to "sample content" **Example** -```js const NewFileContentManager = brackets.getModule("features/NewFileContentManager"); // replace `js` with language ID(Eg. javascript) if you want to restrict the preview to js files only. use `all` for // all languages. NewFileContentManager.registerContentProvider(exports, ["js"], 1); // provide a helpful name for the ContentProvider. This will be useful if you have to debug. exports.CONTENT_PROVIDER_NAME = "extension.someName"; // now implement the getContent function that will be invoked when ever user creates a new empty file. exports.getContent = function(fullPath) { return new Promise((resolve, reject)=>{ resolve("sample content"); }); }; ``` ## API ### registerContentProvider Register a Content provider with this api. +```js +const NewFileContentManager = brackets.getModule("features/NewFileContentManager"); +// replace `js` with language ID(Eg. javascript) if you want to restrict the preview to js files only. use `all` for +// all languages. +NewFileContentManager.registerContentProvider(exports, ["js"], 1); + +// provide a helpful name for the ContentProvider. This will be useful if you have to debug. +exports.CONTENT_PROVIDER_NAME = "extension.someName"; +// now implement the getContent function that will be invoked when ever user creates a new empty file. +exports.getContent = function(fullPath) { + return new Promise((resolve, reject)=>{ + resolve("sample content"); + }); + }; +``` + +## API +### registerContentProvider +Register a Content provider with this api. **Example** -```js // syntax NewFileContentManager.registerContentProvider(provider, supportedLanguages, priority); ``` The API requires three parameters: 1. `provider`: must implement a `getContent` function which will be invoked to get the content. See API doc below. 1. `supportedLanguages`: An array of languages that the provider supports. If `["all"]` is supplied, then the provider will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` 1. `priority`: Contents provided hy providers with higher priority will win if there are more than one provider registered for the language. Default is 0. +```js +// syntax +NewFileContentManager.registerContentProvider(provider, supportedLanguages, priority); +``` +The API requires three parameters: +1. `provider`: must implement a `getContent` function which will be invoked to get the content. See API doc below. +1. `supportedLanguages`: An array of languages that the provider supports. If `["all"]` is supplied, then the + provider will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` +1. `priority`: Contents provided hy providers with higher priority will win if there are more than + one provider registered for the language. Default is 0. **Example** -```js // to register a provider that will be invoked for all languages. where provider is any object that implements // a getContent function NewFileContentManager.registerContentProvider(provider, ["all"]); // to register a provider that will be invoked for specific languages NewFileContentManager.registerContentProvider(provider, ["javascript", "html", "php"]); ``` ### removeContentProvider Removes a registered content provider. The API takes the same arguments as `registerContentProvider`. +```js +// to register a provider that will be invoked for all languages. where provider is any object that implements +// a getContent function +NewFileContentManager.registerContentProvider(provider, ["all"]); + +// to register a provider that will be invoked for specific languages +NewFileContentManager.registerContentProvider(provider, ["javascript", "html", "php"]); +``` + +### removeContentProvider +Removes a registered content provider. The API takes the same arguments as `registerContentProvider`. **Example** -```js // syntax NewFileContentManager.removeContentProvider(provider, supportedLanguages); // Example NewFileContentManager.removeContentProvider(provider, ["javascript", "html"]); ``` ### provider.getContent Each provider must implement the `getContent` function that returns a promise. The promise either resolves with the content text or rejects if there is no content made available by the provider. +```js +// syntax +NewFileContentManager.removeContentProvider(provider, supportedLanguages); +// Example +NewFileContentManager.removeContentProvider(provider, ["javascript", "html"]); +``` + +### provider.getContent +Each provider must implement the `getContent` function that returns a promise. The promise either resolves with +the content text or rejects if there is no content made available by the provider. **Example** -```js exports.CONTENT_PROVIDER_NAME = "extension.someName"; // for debugging // function signature exports.getContent = function(fullPath) { return new Promise((resolve, reject)=>{ resolve("sample content"); }); }; ``` #### parameters The function will be called with the path of the file that needs the content. 1. `fullPath` - string path #### return types A promise that resolves with the content text or rejects if there is no content made available by the provider. +```js +exports.CONTENT_PROVIDER_NAME = "extension.someName"; // for debugging +// function signature +exports.getContent = function(fullPath) { + return new Promise((resolve, reject)=>{ + resolve("sample content"); + }); + }; +``` + +#### parameters +The function will be called with the path of the file that needs the content. +1. `fullPath` - string path + +#### return types +A promise that resolves with the content text or rejects if there is no content made available by the provider. ### features/NewFileContentManager.getInitialContentForFile(fullPath) ⇒ Promise.<string> -Returns a promise that resolves to the default text content of the given file after querying all the content providers. If no text is returned by any providers, it will return an empty string "". To get the default content given a path NewFileContentManager.getInitialContentForFile("/path/to/file.jsx"); +Returns a promise that resolves to the default text content of the given file after querying +all the content providers. If no text is returned by any providers, it will return an empty string "". +To get the default content given a path +NewFileContentManager.getInitialContentForFile("/path/to/file.jsx"); **Kind**: inner method of [features/NewFileContentManager](#module_features/NewFileContentManager) **Returns**: Promise.<string> - The text contents diff --git a/docs/API-Reference/features/QuickViewManager.md b/docs/API-Reference/features/QuickViewManager.md index 8a287244ec..38cf0cbfa5 100644 --- a/docs/API-Reference/features/QuickViewManager.md +++ b/docs/API-Reference/features/QuickViewManager.md @@ -6,20 +6,164 @@ const QuickViewManager = brackets.getModule("features/QuickViewManager") ## features/QuickViewManager -QuickViewManager provides support to add interactive preview popups on hover over the main editors. Extensions can register to provide previews with `QuickViewManager.registerQuickViewProvider` API. Phoenix code quick view Phoenix code quick view Youtube ### See Related: SelectionViewManager [features/SelectionViewManager](https://github.com/phcode-dev/phoenix/wiki/SelectionViewManager-API) is similar to QuickViewManager API. * SelectionViews popup only once user selects a text by mouse or hover over a region with text selection. * Quickviews popup on mouse hover. quick view pops on mouse hover ## Usage Lets build a "hello world" extension that displays "hello world" on hover over a text in the editor. In your extension file, add the following code: +QuickViewManager provides support to add interactive preview popups on hover over the main editors. +Extensions can register to provide previews with `QuickViewManager.registerQuickViewProvider` API. +Phoenix code quick view +Phoenix code quick view Youtube + +### See Related: SelectionViewManager +[features/SelectionViewManager](https://github.com/phcode-dev/phoenix/wiki/SelectionViewManager-API) is similar to +QuickViewManager API. +* SelectionViews popup only once user selects a text by mouse or hover over a region with text selection. +* Quickviews popup on mouse hover. +quick view pops on mouse hover + + +## Usage +Lets build a "hello world" extension that displays "hello world" on hover over a text in the editor. +In your extension file, add the following code: **Example** -```js const QuickViewManager = brackets.getModule("features/QuickViewManager"); // replace `all` with language ID(Eg. javascript) if you want to restrict the preview to js files only. QuickViewManager.registerQuickViewProvider(exports, ["all"]); // provide a helpful name for the QuickView. This will be useful if you implement `filterQuickView` function or // have to debug the quick view. exports.QUICK_VIEW_NAME = "extension.someName"; // now implement the getQuickView function that will be invoked when ever user hovers over a text in the editor. exports.getQuickView = function(editor, pos, token, line) { return new Promise((resolve, reject)=>{ resolve({ start: {line: pos.line, ch:token.start}, end: {line: pos.line, ch:token.end}, content: "
hello world
" }); }); }; // optional filter quick view function to handle multiple quick views exports.filterQuickView = function(popovers){ // popovers will be an array of all popovers rendered by providers return popovers; // dont filter show everything in this case } ``` ### How it works When QuickViewManager determines that the user intents to see QuickView on hover, `getQuickView` function on all registered QuickView providers are invoked to get the quick view popup. `getQuickView` should return a promise that resolves to the popup contents if the provider has a quick view. Else just reject the promise. If multiple providers returns QuickView, all of them are displayed stacked one by one. You can alter this behavior by providing a `filterQuickView` function in the provider where you can modify what previews will be shown. See detailed API docs for implementation details below: ## API ### registerQuickViewProvider Register a QuickView provider with this api. +```js +const QuickViewManager = brackets.getModule("features/QuickViewManager"); +// replace `all` with language ID(Eg. javascript) if you want to restrict the preview to js files only. +QuickViewManager.registerQuickViewProvider(exports, ["all"]); + +// provide a helpful name for the QuickView. This will be useful if you implement `filterQuickView` function or +// have to debug the quick view. +exports.QUICK_VIEW_NAME = "extension.someName"; +// now implement the getQuickView function that will be invoked when ever user hovers over a text in the editor. +exports.getQuickView = function(editor, pos, token, line) { + return new Promise((resolve, reject)=>{ + resolve({ + start: {line: pos.line, ch:token.start}, + end: {line: pos.line, ch:token.end}, + content: "
hello world
" + }); + }); + }; +// optional filter quick view function to handle multiple quick views +exports.filterQuickView = function(popovers){ + // popovers will be an array of all popovers rendered by providers + return popovers; // dont filter show everything in this case +} +``` + +### How it works +When QuickViewManager determines that the user intents to see QuickView on hover, `getQuickView` function on all +registered QuickView providers are invoked to get the quick view popup. `getQuickView` should return a promise +that resolves to the popup contents if the provider has a quick view. Else just reject the promise. If multiple +providers returns QuickView, all of them are displayed stacked one by one. You can alter this behavior by +providing a `filterQuickView` function in the provider where you can modify what previews will be shown. +See detailed API docs for implementation details below: + +## API +### registerQuickViewProvider +Register a QuickView provider with this api. **Example** -```js // syntax QuickViewManager.registerQuickViewProvider(provider, supportedLanguages); ``` The API requires two parameters: 1. `provider`: must implement a `getQuickView` function which will be invoked to get the preview. See API doc below. 1. `supportedLanguages`: An array of languages that the QuickView supports. If `["all"]` is supplied, then the QuickView will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` +```js +// syntax +QuickViewManager.registerQuickViewProvider(provider, supportedLanguages); +``` +The API requires two parameters: +1. `provider`: must implement a `getQuickView` function which will be invoked to get the preview. See API doc below. +1. `supportedLanguages`: An array of languages that the QuickView supports. If `["all"]` is supplied, then the + QuickView will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` **Example** -```js // to register a provider that will be invoked for all languages. where provider is any object that implements // a getQuickView function QuickViewManager.registerQuickViewProvider(provider, ["all"]); // to register a provider that will be invoked for specific languages QuickViewManager.registerQuickViewProvider(provider, ["javascript", "html", "php"]); ``` ### removeQuickViewProvider Removes a registered QuickView provider. The API takes the same arguments as `registerQuickViewProvider`. +```js +// to register a provider that will be invoked for all languages. where provider is any object that implements +// a getQuickView function +QuickViewManager.registerQuickViewProvider(provider, ["all"]); + +// to register a provider that will be invoked for specific languages +QuickViewManager.registerQuickViewProvider(provider, ["javascript", "html", "php"]); +``` + +### removeQuickViewProvider +Removes a registered QuickView provider. The API takes the same arguments as `registerQuickViewProvider`. **Example** -```js // syntax QuickViewManager.removeQuickViewProvider(provider, supportedLanguages); // Example QuickViewManager.removeQuickViewProvider(provider, ["javascript", "html"]); ``` ### getQuickView Each provider must implement the `getQuickView` function that returns a promise. The promise either resolves with the quick view details object(described below) or rejects if there is no preview for the position. +```js +// syntax +QuickViewManager.removeQuickViewProvider(provider, supportedLanguages); +// Example +QuickViewManager.removeQuickViewProvider(provider, ["javascript", "html"]); +``` + +### getQuickView +Each provider must implement the `getQuickView` function that returns a promise. The promise either resolves with +the quick view details object(described below) or rejects if there is no preview for the position. **Example** -```js // function signature provider.getQuickView = function(editor, pos, token, line) { return new Promise((resolve, reject)=>{ resolve({ start: {line: pos.line, ch:token.start}, end: {line: pos.line, ch:token.end}, content: "
hello world
", editsDoc: false // this is optional if the quick view edits the current doc }); }); }; ``` #### parameters The function will be called with the following arguments: 1. `editor` - The editor over which the user hovers the mouse cursor. 1. `pos` - the cursor position over which the user hovers. 1. `token` - hovered token details 1. `line` - the full line text as string. #### return types The promise returned should resolve to an object with the following contents: 1. `start` : Indicates the start cursor position from which the quick view is valid. 1. `end` : Indicates the end cursor position to which the quick view is valid. These are generally used to highlight the hovered section of the text in the editor. 1. `content`: Either `HTML` as text, a `DOM Node` or a `Jquery Element`. 1. `editsDoc`: Optional, set to true if the quick view can edit the active document. #### Modifying the QuickView content after resolving `getQuickView` promise Some advanced/interactive extensions may need to do dom operations on the quick view content. In such cases, it is advised to return a domNode/Jquery element as content in `getQuickView`. Event Handlers or further dom manipulations can be done on the returned content element. The Quick view may be dismissed at any time, so be sure to check if the DOM Node is visible in the editor before performing any operations. #### Considerations 1. QuickView won't be displayed till all provider promises are settled. To improve performance, if your QuickView handler takes time to resolve the QuickView, resolve a dummy quick once you are sure that a QuickView needs to be shown to the user. The div contents can be later updated as and when more details are available. 1. Note that the QuickView could be hidden/removed any time by the QuickViewManager. 1. If multiple providers returns a valid popup, all of them are displayed except if the `filterQuickView` modifies the quick view render list. Note that `filterQuickView` is called only for those providers that provided a quick view. ### filterQuickView Each provider can optionally implement the `filterQuickView` function to control what among the available quick views should be rendered if multiple providers responded with a QuickView. The function will be called once all `getQuickView` providers provided a valid preview object. +```js +// function signature +provider.getQuickView = function(editor, pos, token, line) { + return new Promise((resolve, reject)=>{ + resolve({ + start: {line: pos.line, ch:token.start}, + end: {line: pos.line, ch:token.end}, + content: "
hello world
", + editsDoc: false // this is optional if the quick view edits the current doc + }); + }); + }; +``` + +#### parameters +The function will be called with the following arguments: +1. `editor` - The editor over which the user hovers the mouse cursor. +1. `pos` - the cursor position over which the user hovers. +1. `token` - hovered token details +1. `line` - the full line text as string. + +#### return types +The promise returned should resolve to an object with the following contents: +1. `start` : Indicates the start cursor position from which the quick view is valid. +1. `end` : Indicates the end cursor position to which the quick view is valid. These are generally used to highlight + the hovered section of the text in the editor. +1. `content`: Either `HTML` as text, a `DOM Node` or a `Jquery Element`. +1. `editsDoc`: Optional, set to true if the quick view can edit the active document. + +#### Modifying the QuickView content after resolving `getQuickView` promise +Some advanced/interactive extensions may need to do dom operations on the quick view content. +In such cases, it is advised to return a domNode/Jquery element as content in `getQuickView`. Event Handlers +or further dom manipulations can be done on the returned content element. +The Quick view may be dismissed at any time, so be sure to check if the DOM Node is visible in the editor before +performing any operations. + +#### Considerations +1. QuickView won't be displayed till all provider promises are settled. To improve performance, if your QuickView + handler takes time to resolve the QuickView, resolve a dummy quick once you are sure that a QuickView needs + to be shown to the user. The div contents can be later updated as and when more details are available. +1. Note that the QuickView could be hidden/removed any time by the QuickViewManager. +1. If multiple providers returns a valid popup, all of them are displayed except if the `filterQuickView` modifies + the quick view render list. Note that `filterQuickView` is called only for those providers that + provided a quick view. + +### filterQuickView +Each provider can optionally implement the `filterQuickView` function to control what among the available +quick views should be rendered if multiple providers responded with a QuickView. The function will be called +once all `getQuickView` providers provided a valid preview object. **Example** -```js // function signature provider.filterQuickView = function(popovers) { for(let popover of popovers){ // here if we see that a quick view with name `exclusiveQuickView` is present, then we only show that // QuickView. popover.providerInfo object holds details of what provider provided the quick view. if(popover.providerInfo.provider.QUICK_VIEW_NAME === "exclusiveQuickView"){ return [popover] } } // if nothing is returned, then the `popovers` param will be used to show popover }; ``` #### parameter The function will be called with the `popovers` parameter which is an array of popover objects that was returned by `getQuickView` function of all succeeded providers. Details of each provider that created a popover will be present in `popovers[i].providerInfo` object. #### return An array of popovers that needs to be rendered, or nothing(to render the original popover parameter as is). +```js +// function signature +provider.filterQuickView = function(popovers) { + for(let popover of popovers){ + // here if we see that a quick view with name `exclusiveQuickView` is present, then we only show that + // QuickView. popover.providerInfo object holds details of what provider provided the quick view. + if(popover.providerInfo.provider.QUICK_VIEW_NAME === "exclusiveQuickView"){ + return [popover] + } + } + // if nothing is returned, then the `popovers` param will be used to show popover + }; +``` + +#### parameter +The function will be called with the `popovers` parameter which is an array of popover objects that was returned +by `getQuickView` function of all succeeded providers. Details of each provider that created a popover +will be present in `popovers[i].providerInfo` object. + +#### return +An array of popovers that needs to be rendered, or nothing(to render the original popover parameter as is). * [features/QuickViewManager](#module_features/QuickViewManager) * [.isQuickViewShown()](#module_features/QuickViewManager..isQuickViewShown) ⇒ boolean @@ -35,7 +179,8 @@ If quickview is displayed and visible on screen ### features/QuickViewManager.lockQuickView() : function -locks the current QuickView if shown to be permanently displayed on screen till the `unlockQuickView` function is called or document changes. +locks the current QuickView if shown to be permanently displayed on screen till the `unlockQuickView` function +is called or document changes. **Kind**: inner method of [features/QuickViewManager](#module_features/QuickViewManager) diff --git a/docs/API-Reference/features/SelectionViewManager.md b/docs/API-Reference/features/SelectionViewManager.md index 9c97b0b9d2..d64b657585 100644 --- a/docs/API-Reference/features/SelectionViewManager.md +++ b/docs/API-Reference/features/SelectionViewManager.md @@ -6,18 +6,118 @@ const SelectionViewManager = brackets.getModule("features/SelectionViewManager") ## features/SelectionViewManager -SelectionViewManager provides support to add interactive preview popups on selection over the main editors. This can be used to provide interactive editor controls on a selected element. Extensions can register to provide previews with `SelectionViewManager.registerSelectionViewProvider` API. Phoenix code selection view ### See Related: QuickViewManager [features/QuickViewManager](https://github.com/phcode-dev/phoenix/wiki/QuickViewManager-API) is similar to SelectionViewManager API. * SelectionViews popup only once user selects a text by mouse or hover over a region with text selection. * Quickviews popup on mouse hover. Phoenix code selection view Youtube image ## Usage Lets build a "hello world" extension that displays "hello world" above selected text in the editor. In your extension file, add the following code: +SelectionViewManager provides support to add interactive preview popups on selection over the main editors. +This can be used to provide interactive editor controls on a selected element. + +Extensions can register to provide previews with `SelectionViewManager.registerSelectionViewProvider` API. +Phoenix code selection view + + +### See Related: QuickViewManager +[features/QuickViewManager](https://github.com/phcode-dev/phoenix/wiki/QuickViewManager-API) is similar to +SelectionViewManager API. +* SelectionViews popup only once user selects a text by mouse or hover over a region with text selection. +* Quickviews popup on mouse hover. +Phoenix code selection view Youtube image + +## Usage +Lets build a "hello world" extension that displays "hello world" above selected text in the editor. +In your extension file, add the following code: **Example** -```js const SelectionViewManager = brackets.getModule("features/SelectionViewManager"); // replace `all` with language ID(Eg. javascript) if you want to restrict the preview to js files only. SelectionViewManager.registerSelectionViewProvider(exports, ["all"]); // provide a helpful name for the SelectionView. This will be useful if you have to debug the selection view exports.SELECTION_VIEW_NAME = "extension.someName"; // now implement the getSelectionView function that will be invoked when ever user selection changes in the editor. exports.getSelectionView = function(editor, selections) { return new Promise((resolve, reject)=>{ resolve({ content: "
hello world
" }); }); }; ``` ### How it works When SelectionViewManager determines that the user intents to see SelectionViewr, `getSelectionView` function on all registered SelectionView providers are invoked to get the Selection View popup. `getSelectionView` should return a promise that resolves to the popup contents if the provider has a Selection View. Else just reject the promise. If multiple providers returns SelectionView, all of them are displayed one by one. See detailed API docs for implementation details below: ## API ### registerSelectionViewProvider Register a SelectionView provider with this api. +```js +const SelectionViewManager = brackets.getModule("features/SelectionViewManager"); +// replace `all` with language ID(Eg. javascript) if you want to restrict the preview to js files only. +SelectionViewManager.registerSelectionViewProvider(exports, ["all"]); + +// provide a helpful name for the SelectionView. This will be useful if you have to debug the selection view +exports.SELECTION_VIEW_NAME = "extension.someName"; +// now implement the getSelectionView function that will be invoked when ever user selection changes in the editor. +exports.getSelectionView = function(editor, selections) { + return new Promise((resolve, reject)=>{ + resolve({ + content: "
hello world
" + }); + }); + }; +``` + +### How it works +When SelectionViewManager determines that the user intents to see SelectionViewr, `getSelectionView` function on all +registered SelectionView providers are invoked to get the Selection View popup. `getSelectionView` should return +a promise that resolves to the popup contents if the provider has a Selection View. Else just reject the promise. +If multiple providers returns SelectionView, all of them are displayed one by one. +See detailed API docs for implementation details below: + +## API +### registerSelectionViewProvider +Register a SelectionView provider with this api. **Example** -```js // syntax SelectionViewManager.registerSelectionViewProvider(provider, supportedLanguages); ``` The API requires two parameters: 1. `provider`: must implement a `getSelectionView` function which will be invoked to get the preview. See API doc below. 1. `supportedLanguages`: An array of languages that the SelectionView supports. If `["all"]` is supplied, then the SelectionView will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` +```js +// syntax +SelectionViewManager.registerSelectionViewProvider(provider, supportedLanguages); +``` +The API requires two parameters: +1. `provider`: must implement a `getSelectionView` function which will be invoked to get the preview. See API doc below. +1. `supportedLanguages`: An array of languages that the SelectionView supports. If `["all"]` is supplied, then the + SelectionView will be invoked for all languages. Restrict to specific languages: Eg: `["javascript", "html", "php"]` **Example** -```js // to register a provider that will be invoked for all languages. where provider is any object that implements // a getSelectionView function SelectionViewManager.registerSelectionViewProvider(provider, ["all"]); // to register a provider that will be invoked for specific languages SelectionViewManager.registerSelectionViewProvider(provider, ["javascript", "html", "php"]); ``` ### removeSelectionViewProvider Removes a registered SelectionView provider. The API takes the same arguments as `registerSelectionViewProvider`. +```js +// to register a provider that will be invoked for all languages. where provider is any object that implements +// a getSelectionView function +SelectionViewManager.registerSelectionViewProvider(provider, ["all"]); + +// to register a provider that will be invoked for specific languages +SelectionViewManager.registerSelectionViewProvider(provider, ["javascript", "html", "php"]); +``` + +### removeSelectionViewProvider +Removes a registered SelectionView provider. The API takes the same arguments as `registerSelectionViewProvider`. **Example** -```js // syntax SelectionViewManager.removeSelectionViewProvider(provider, supportedLanguages); // Example SelectionViewManager.removeSelectionViewProvider(provider, ["javascript", "html"]); ``` ### getSelectionView Each provider must implement the `getSelectionView` function that returns a promise. The promise either resolves with the Selection View details object(described below) or rejects if there is no preview for the position. +```js +// syntax +SelectionViewManager.removeSelectionViewProvider(provider, supportedLanguages); +// Example +SelectionViewManager.removeSelectionViewProvider(provider, ["javascript", "html"]); +``` + +### getSelectionView +Each provider must implement the `getSelectionView` function that returns a promise. The promise either resolves with +the Selection View details object(described below) or rejects if there is no preview for the position. **Example** -```js // function signature provider.getSelectionView = function(editor, selections) { return new Promise((resolve, reject)=>{ resolve({ content: "
hello world
" }); }); }; ``` #### parameters The function will be called with the following arguments: 1. `editor` - The editor over which the user hovers the mouse cursor. 1. `selections` - An array containing the active selections when the selection view was trigerred. #### return types The promise returned should resolve to an object with the following contents: 1. `content`: Either `HTML` as text, a `DOM Node` or a `Jquery Element`. #### Modifying the SelectionView content after resolving `getSelectionView` promise Some advanced/interactive extensions may need to do dom operations on the SelectionView content. In such cases, it is advised to return a domNode/Jquery element as content in `getSelectionView`. Event Handlers or further dom manipulations can be done on the returned content element. The SelectionView may be dismissed at any time, so be sure to check if the DOM Node is visible in the editor before performing any operations. #### Considerations 1. SelectionView won't be displayed till all provider promises are settled. To improve performance, if your SelectionView handler takes time to resolve the SelectionView, resolve a dummy quick once you are sure that a SelectionView needs to be shown to the user. The div contents can be later updated as and when more details are available. 1. Note that the SelectionView could be hidden/removed any time by the SelectionViewManager. 1. If multiple providers returns a valid popup, all of them are displayed. +```js +// function signature +provider.getSelectionView = function(editor, selections) { + return new Promise((resolve, reject)=>{ + resolve({ + content: "
hello world
" + }); + }); + }; +``` + +#### parameters +The function will be called with the following arguments: +1. `editor` - The editor over which the user hovers the mouse cursor. +1. `selections` - An array containing the active selections when the selection view was trigerred. + +#### return types +The promise returned should resolve to an object with the following contents: +1. `content`: Either `HTML` as text, a `DOM Node` or a `Jquery Element`. + +#### Modifying the SelectionView content after resolving `getSelectionView` promise +Some advanced/interactive extensions may need to do dom operations on the SelectionView content. +In such cases, it is advised to return a domNode/Jquery element as content in `getSelectionView`. Event Handlers +or further dom manipulations can be done on the returned content element. +The SelectionView may be dismissed at any time, so be sure to check if the DOM Node is visible in the editor before +performing any operations. + +#### Considerations +1. SelectionView won't be displayed till all provider promises are settled. To improve performance, if your SelectionView + handler takes time to resolve the SelectionView, resolve a dummy quick once you are sure that a SelectionView needs + to be shown to the user. The div contents can be later updated as and when more details are available. +1. Note that the SelectionView could be hidden/removed any time by the SelectionViewManager. +1. If multiple providers returns a valid popup, all of them are displayed. ### features/SelectionViewManager.isSelectionViewShown() ⇒ boolean diff --git a/docs/API-Reference/language/HTMLDOMDiff.md b/docs/API-Reference/language/HTMLDOMDiff.md index 1be77da4b1..4700a1b50b 100644 --- a/docs/API-Reference/language/HTMLDOMDiff.md +++ b/docs/API-Reference/language/HTMLDOMDiff.md @@ -6,7 +6,19 @@ const HTMLDOMDiff = brackets.getModule("language/HTMLDOMDiff") ## domdiff(oldNode, newNode) ⇒ Array.<Object> -Generate a list of edits that will mutate oldNode to look like newNode. Currently, there are the following possible edit operations: - elementInsert - elementDelete - elementMove - textInsert - textDelete - textReplace - attrDelete - attrChange - attrAdd - rememberNodes (a special instruction that reflects the need to hang on to moved nodes) +Generate a list of edits that will mutate oldNode to look like newNode. +Currently, there are the following possible edit operations: + +- elementInsert +- elementDelete +- elementMove +- textInsert +- textDelete +- textReplace +- attrDelete +- attrChange +- attrAdd +- rememberNodes (a special instruction that reflects the need to hang on to moved nodes) **Kind**: global function **Returns**: Array.<Object> - - List of edit operations. @@ -24,7 +36,10 @@ Generate a list of edits that will mutate oldNode to look like newNode. Currentl ### domdiff.queuePush() -Adds elements to the queue for generateChildEdits. Only elements (and not text nodes) are added. New nodes (ones that aren't in the old nodeMap), are not added here because they will be added when generateChildEdits creates the elementInsert edit. +Adds elements to the queue for generateChildEdits. +Only elements (and not text nodes) are added. New nodes (ones that aren't in the +old nodeMap), are not added here because they will be added when generateChildEdits +creates the elementInsert edit. **Kind**: inner method of [domdiff](#domdiff) diff --git a/docs/API-Reference/search/QuickOpenHelper.md b/docs/API-Reference/search/QuickOpenHelper.md index 88d7236f52..d0bd79392d 100644 --- a/docs/API-Reference/search/QuickOpenHelper.md +++ b/docs/API-Reference/search/QuickOpenHelper.md @@ -16,7 +16,8 @@ const QuickOpenHelper = brackets.getModule("search/QuickOpenHelper") ## itemFocus(selectedItem, query, explicit) -Scroll to the selected item in the current document (unless no query string entered yet, in which case the topmost list item is irrelevant) +Scroll to the selected item in the current document (unless no query string entered yet, +in which case the topmost list item is irrelevant) **Kind**: global function @@ -29,7 +30,8 @@ Scroll to the selected item in the current document (unless no query string ente ## itemSelect(selectedItem, query) -Scroll to the selected item in the current document (unless no query string entered yet, in which case the topmost list item is irrelevant) +Scroll to the selected item in the current document (unless no query string entered yet, +in which case the topmost list item is irrelevant) **Kind**: global function diff --git a/docs/API-Reference/search/SearchModel.md b/docs/API-Reference/search/SearchModel.md index 86e0f0da1b..61bb118719 100644 --- a/docs/API-Reference/search/SearchModel.md +++ b/docs/API-Reference/search/SearchModel.md @@ -3,9 +3,15 @@ const SearchModel = brackets.getModule("search/SearchModel") ``` - + -## Manages a set of search query and result data. Dispatches these events: change - whenever the results have been updated. Note that its up to people who edit the model to call fireChange() when necessary - it doesnt automatically fire. +## Manages a set of search query and result data. +Dispatches these events: + change - whenever the results have been updated. Note that its up to people who + edit the model to call fireChange() when necessary - it doesnt automatically fire. **Kind**: global class diff --git a/docs/API-Reference/view/PluginPanelView.md b/docs/API-Reference/view/PluginPanelView.md index a3e0401543..81c93130a1 100644 --- a/docs/API-Reference/view/PluginPanelView.md +++ b/docs/API-Reference/view/PluginPanelView.md @@ -51,7 +51,8 @@ Determines if the panel is visible ### panel.registerCanBeShownHandler(canShowHandlerFn) ⇒ boolean -Registers a call back function that will be called just before panel is shown. The handler should return true if the panel can be shown, else return false and the panel will not be shown. +Registers a call back function that will be called just before panel is shown. The handler should return true +if the panel can be shown, else return false and the panel will not be shown. **Kind**: instance method of [Panel](#Panel) **Returns**: boolean - true if visible, false if not @@ -69,7 +70,8 @@ Returns true if th panel can be shown, else false. ### panel.registerOnCloseRequestedHandler(handler) -Registers an async handler that is called before the panel is closed via user interaction. The handler should return `true` to allow the close, or `false` to prevent it. +Registers an async handler that is called before the panel is closed via user interaction. +The handler should return `true` to allow the close, or `false` to prevent it. **Kind**: instance method of [Panel](#Panel) @@ -80,7 +82,9 @@ Registers an async handler that is called before the panel is closed via user in ### panel.requestClose() ⇒ Promise.<boolean> -Requests the panel to hide, invoking the registered onCloseRequested handler first (if any). If the handler returns false, the panel stays open. If it returns true or no handler is registered, `hide()` is called. +Requests the panel to hide, invoking the registered onCloseRequested handler first (if any). +If the handler returns false, the panel stays open. If it returns true or no handler is +registered, `hide()` is called. **Kind**: instance method of [Panel](#Panel) **Returns**: Promise.<boolean> - Resolves to true if the panel was hidden, false if prevented. diff --git a/docs/API-Reference/widgets/PopUpManager.md b/docs/API-Reference/widgets/PopUpManager.md index 01621c697f..5b02705436 100644 --- a/docs/API-Reference/widgets/PopUpManager.md +++ b/docs/API-Reference/widgets/PopUpManager.md @@ -28,7 +28,8 @@ Add Esc key handling for a popup DOM element. ## removePopUp($popUp) -Remove Esc key handling for a pop-up. Removes the pop-up from the DOM if the pop-up is currently visible and was not originally attached. +Remove Esc key handling for a pop-up. Removes the pop-up from the DOM +if the pop-up is currently visible and was not originally attached. **Kind**: global function @@ -63,7 +64,9 @@ Selects the next or previous item in the popup. ## listenToContextMenu(contextMenu) -Context menus are also created in AppInit.htmlReady(), so they may not yet have been created when we get our AppInit.htmlReady() callback, so we provide this method to tell us when to start listening for their events +Context menus are also created in AppInit.htmlReady(), so they may not +yet have been created when we get our AppInit.htmlReady() callback, so +we provide this method to tell us when to start listening for their events **Kind**: global function diff --git a/docs/API-Reference/widgets/StatusBar.md b/docs/API-Reference/widgets/StatusBar.md index 50841e74a1..8a6f591c4b 100644 --- a/docs/API-Reference/widgets/StatusBar.md +++ b/docs/API-Reference/widgets/StatusBar.md @@ -6,7 +6,10 @@ const StatusBar = brackets.getModule("widgets/StatusBar") ## AppInit -A status bar with support for file information and busy and status indicators. This is a semi-generic container; for the code that decides what content appears in the status bar, see client modules like EditorStatusBar. (Although in practice StatusBar's HTML structure and initialization assume it's only used for this one purpose, and all the APIs are on a singleton). +A status bar with support for file information and busy and status indicators. This is a semi-generic +container; for the code that decides what content appears in the status bar, see client modules like +EditorStatusBar. (Although in practice StatusBar's HTML structure and initialization +assume it's only used for this one purpose, and all the APIs are on a singleton). **Kind**: global variable diff --git a/docs/API-Reference/worker/IndexingWorker.md b/docs/API-Reference/worker/IndexingWorker.md index 1056c5869d..1d7fa08430 100644 --- a/docs/API-Reference/worker/IndexingWorker.md +++ b/docs/API-Reference/worker/IndexingWorker.md @@ -6,10 +6,41 @@ const IndexingWorker = brackets.getModule("worker/IndexingWorker") ## worker/IndexingWorker -Phoenix houses a file indexing worker which caches all cacheable files of a project in memory. This module can be used to communicate with the Index and extend it by attaching new js worker scripts to the indexing worker as discussed below. Any extension that works on a large number of files should use the indexing worker cache to free up the main thread of heavy file access. * Extensions performing large compute tasks should create their own worker and may use easy util methods in [worker/WorkerComm](./WorkerComm) to communicate with the web worker. ## Extending the indexing worker You can add your own custom scripts to the indexing worker by following the below example. Suppose you have an extension folder with the following structure: ``` myExtensionFolder │ my_worker.js // the script that you need to attach to the web worker │ main.js ``` In `main.js` extension module, we can import `my_worker.js` script into `IndexingWorker` by: +Phoenix houses a file indexing worker which caches all cacheable files of a project in memory. +This module can be used to communicate with the Index and extend it by attaching new js worker scripts to the +indexing worker as discussed below. Any extension that works on a large number of files should use the indexing +worker cache to free up the main thread of heavy file access. + +* Extensions performing large compute tasks should create their own worker and may use easy util methods in + [worker/WorkerComm](./WorkerComm) to communicate with the web worker. + +## Extending the indexing worker +You can add your own custom scripts to the indexing worker by following the below example. Suppose you have an +extension folder with the following structure: +``` +myExtensionFolder +│ my_worker.js // the script that you need to attach to the web worker +│ main.js +``` +In `main.js` extension module, we can import `my_worker.js` script into `IndexingWorker` by: **Example** -```js let ExtensionUtils = brackets.getModule("utils/ExtensionUtils"); let workerPath = ExtensionUtils.getModulePath(module, "my_worker.js") IndexingWorker.loadScriptInWorker(workerPath); ``` Once the worker script is loaded with the above step: * Phoenix can communicate with worker using the `IndexingWorker` reference in Phoenix. * Worker can communicate with Phoenix with the global `WorkerComm` reference within the Indexing worker. All utility methods in module [worker/WorkerComm](./WorkerComm) can be used for worker communication. A global constant `Phoenix.baseURL` is available in the worker context to get the base url from which phoenix was launched. NB: You can use all util methods available in `worker/WorkerComm` as `IndexingWorker` internally uses `WorkerComm` to communicate with the underlying worker thread. +```js +let ExtensionUtils = brackets.getModule("utils/ExtensionUtils"); +let workerPath = ExtensionUtils.getModulePath(module, "my_worker.js") +IndexingWorker.loadScriptInWorker(workerPath); +``` + +Once the worker script is loaded with the above step: +* Phoenix can communicate with worker using the `IndexingWorker` reference in Phoenix. +* Worker can communicate with Phoenix with the global `WorkerComm` reference within the Indexing worker. +All utility methods in module [worker/WorkerComm](./WorkerComm) can be used for worker communication. + +A global constant `Phoenix.baseURL` is available in the worker context to get the base url from which phoenix was +launched. + +NB: You can use all util methods available in `worker/WorkerComm` as `IndexingWorker` internally uses `WorkerComm` +to communicate with the underlying worker thread. * [worker/IndexingWorker](#module_worker/IndexingWorker) * [.WorkerComm](#module_worker/IndexingWorker..WorkerComm) @@ -20,7 +51,29 @@ Phoenix houses a file indexing worker which caches all cacheable files of a proj ### worker/IndexingWorker.WorkerComm -To communicate between the IndexingWorker and Phoenix, the following methods are available: `loadScriptInWorker`, `execPeer`, `setExecHandler`, `triggerPeer` and other APIs described in module `worker/WorkerComm`. The above methods can be used with either `IndexingWorker` reference within Phoenix or the global `WorkerComm` reference within the Indexing worker. (See example below.) See [worker/WorkerComm](./WorkerComm) for detailed API docs. ```js // To Execute a named function `extensionName.sayHello` in the worker from phoenix // in my_worker.js. It is a good practice to prefix your `[extensionName]` // to exec handler to prevent name collisions with other extensions. WorkerComm.setExecHandler("extensionName.sayHello", (arg)=>{ console.log("hello from worker ", arg); // prints "hello from worker phoenix" return "Hello Phoenix"; }); // In Phoenix/extension let workerMessage = await IndexingWorker.execPeer("extensionName.sayHello", "phoenix"); console.log(workerMessage); // prints "Hello Phoenix" ``` +To communicate between the IndexingWorker and Phoenix, the following methods are available: +`loadScriptInWorker`, `execPeer`, `setExecHandler`, `triggerPeer` and other APIs described +in module `worker/WorkerComm`. +The above methods can be used with either `IndexingWorker` reference within Phoenix +or the global `WorkerComm` reference within the Indexing worker. (See example below.) + +See [worker/WorkerComm](./WorkerComm) for detailed API docs. + +```js +// To Execute a named function `extensionName.sayHello` in the worker from phoenix + +// in my_worker.js. It is a good practice to prefix your `[extensionName]` +// to exec handler to prevent name collisions with other extensions. + +WorkerComm.setExecHandler("extensionName.sayHello", (arg)=>{ + console.log("hello from worker ", arg); // prints "hello from worker phoenix" + return "Hello Phoenix"; +}); + +// In Phoenix/extension +let workerMessage = await IndexingWorker.execPeer("extensionName.sayHello", "phoenix"); +console.log(workerMessage); // prints "Hello Phoenix" +``` **Kind**: inner property of [worker/IndexingWorker](#module_worker/IndexingWorker) @@ -32,7 +85,8 @@ Raised when crawling started in the indexing worker. ### "EVENT_CRAWL_PROGRESS" -Raised when crawling in progressing within the worker. The handler will receive the following properties as parameter. +Raised when crawling in progressing within the worker. The handler will receive the +following properties as parameter. **Kind**: event emitted by [worker/IndexingWorker](#module_worker/IndexingWorker) **Properties** @@ -45,7 +99,8 @@ Raised when crawling in progressing within the worker. The handler will receive ### "EVENT_CRAWL_COMPLETE" -Raised when crawling is complete within the worker. The handler will receive the following properties as parameter. +Raised when crawling is complete within the worker. The handler will receive the +following properties as parameter. **Kind**: event emitted by [worker/IndexingWorker](#module_worker/IndexingWorker) **Properties** diff --git a/docs/API-Reference/worker/WorkerComm.md b/docs/API-Reference/worker/WorkerComm.md index 4ea44579c0..2d4e2f5b82 100644 --- a/docs/API-Reference/worker/WorkerComm.md +++ b/docs/API-Reference/worker/WorkerComm.md @@ -6,12 +6,53 @@ const WorkerComm = brackets.getModule("worker/WorkerComm") ## worker/WorkerComm -WorkerComm provides util methods to communicate between web workers and Phoenix. This module can be loaded from within web-workers and a phoenix extension that loads the web-worker. ### Creating a WebWorker from your extension and attaching `WorkerComm` to it. See an example extension code below that creates its own web worker and uses `WorkerComm` for communication. +WorkerComm provides util methods to communicate between web workers and Phoenix. +This module can be loaded from within web-workers and a phoenix extension that loads the web-worker. + +### Creating a WebWorker from your extension and attaching `WorkerComm` to it. +See an example extension code below that creates its own web worker and uses `WorkerComm` for communication. **Example** -```js // from within an extension const WorkerComm = brackets.getModule("worker/WorkerComm"), EventDispatcher = brackets.getModule("utils/EventDispatcher"), ExtensionUtils = brackets.getModule("utils/ExtensionUtils"); // figure out the path of the web worker relative to your extension let workerPath = ExtensionUtils.getModulePath(module, "my_worker_path_within_extension.js") // we need to pass in the `workerCommUrl` so that the web-worker can // load`WorkerComm` within the worker context as described below. let workerCommUrl = `${Phoenix.baseURL}worker/WorkerComm.js`; let eventDispatcherURL = `${Phoenix.baseURL}utils/EventDispatcher.js`; // load the worker const _myWorker = new Worker( `${workerPath}?workerCommUrl=${workerCommUrl}&eventDispatcherURL=${eventDispatcherURL}`); // Not create a `WorkerComm` object and attach to your extension module exports. EventDispatcher.makeEventDispatcher(exports); // all WorkerComm objects needs to be an EventDispatcher. WorkerComm.createWorkerComm(_myWorker, exports); // Now `exports` can be used to communicate with the web-worker // using `WorkerComm` APIs listed below. ``` ### Loading `WorkerComm` from within your webWorker The Web Worker we created above also needs to load `WorkerComm` to be able to communicate with the `WorkerComm` instance in Phoenix. For this, we need to load `WorkerComm` from the URL parameters. (WorkerComm.js lib url needs to passed in while creating the web worker from Phoenix). +```js +// from within an extension +const WorkerComm = brackets.getModule("worker/WorkerComm"), + EventDispatcher = brackets.getModule("utils/EventDispatcher"), + ExtensionUtils = brackets.getModule("utils/ExtensionUtils"); + +// figure out the path of the web worker relative to your extension +let workerPath = ExtensionUtils.getModulePath(module, "my_worker_path_within_extension.js") + +// we need to pass in the `workerCommUrl` so that the web-worker can +// load`WorkerComm` within the worker context as described below. +let workerCommUrl = `${Phoenix.baseURL}worker/WorkerComm.js`; +let eventDispatcherURL = `${Phoenix.baseURL}utils/EventDispatcher.js`; + +// load the worker +const _myWorker = new Worker( +`${workerPath}?workerCommUrl=${workerCommUrl}&eventDispatcherURL=${eventDispatcherURL}`); + +// Not create a `WorkerComm` object and attach to your extension module exports. +EventDispatcher.makeEventDispatcher(exports); +// all WorkerComm objects needs to be an EventDispatcher. +WorkerComm.createWorkerComm(_myWorker, exports); + +// Now `exports` can be used to communicate with the web-worker +// using `WorkerComm` APIs listed below. +``` + +### Loading `WorkerComm` from within your webWorker +The Web Worker we created above also needs to load `WorkerComm` to be able to communicate with the `WorkerComm` +instance in Phoenix. For this, we need to load `WorkerComm` from the URL parameters. +(WorkerComm.js lib url needs to passed in while creating the web worker from Phoenix). **Example** -```js const urlParams = new URLSearchParams(location.search); importScripts(urlParams.get('workerCommUrl')); importScripts(urlParams.get('eventDispatcherURL')); // After this, a global `WorkerComm` object will be available within the // web-worker that can be used to communicate with Phoenix. ``` ## APIs +```js +const urlParams = new URLSearchParams(location.search); +importScripts(urlParams.get('workerCommUrl')); +importScripts(urlParams.get('eventDispatcherURL')); +// After this, a global `WorkerComm` object will be available within the +// web-worker that can be used to communicate with Phoenix. +``` +## APIs * [worker/WorkerComm](#module_worker/WorkerComm) * [.createWorkerComm(postTarget, eventDispatcher)](#module_worker/WorkerComm..createWorkerComm) : function @@ -20,7 +61,17 @@ WorkerComm provides util methods to communicate between web workers and Phoenix. ### worker/WorkerComm.createWorkerComm(postTarget, eventDispatcher) : function -Adds support for WorkerComm APIs to the provided web-Worker instance. Only available in the main thread. This API should be called immediately after creating the worker in main thread. Create a web-worker with `WorkerComm` in an extension. // load the worker [See API docs for full sample] const _myWorker = new Worker( `${workerPath}?workerCommUrl=${workerCommUrl}&eventDispatcherURL=${eventDispatcherURL}`); // Now create a `WorkerComm` object and attach to your extension module exports. EventDispatcher.makeEventDispatcher(exports); // all WorkerComm objects needs to be an EventDispatcher. WorkerComm.createWorkerComm(_myWorker, exports); +Adds support for WorkerComm APIs to the provided web-Worker instance. Only available in the main thread. +This API should be called immediately after creating the worker in main thread. +Create a web-worker with `WorkerComm` in an extension. +// load the worker [See API docs for full sample] +const _myWorker = new Worker( +`${workerPath}?workerCommUrl=${workerCommUrl}&eventDispatcherURL=${eventDispatcherURL}`); + +// Now create a `WorkerComm` object and attach to your extension module exports. +EventDispatcher.makeEventDispatcher(exports); +// all WorkerComm objects needs to be an EventDispatcher. +WorkerComm.createWorkerComm(_myWorker, exports); **Kind**: inner method of [worker/WorkerComm](#module_worker/WorkerComm) From 1b1da9700ed0ffa18c8f9f899f9800b2cd834b73 Mon Sep 17 00:00:00 2001 From: Pluto Date: Sun, 19 Apr 2026 00:13:04 +0530 Subject: [PATCH 3/3] feat: use jsdoc to markdown as library instead of cli --- build/api-docs-generator.js | 98 ++++++++----------------------------- 1 file changed, 21 insertions(+), 77 deletions(-) diff --git a/build/api-docs-generator.js b/build/api-docs-generator.js index d1215c93b3..bac0e639cb 100644 --- a/build/api-docs-generator.js +++ b/build/api-docs-generator.js @@ -3,17 +3,14 @@ const path = require('path'); const glob = require('glob'); const util = require('util'); const crypto = require('crypto'); -const exec = util.promisify(require('child_process').exec); +const jsdoc2md = require('jsdoc-to-markdown'); // Promisify the glob function to enable async/await usage const globPromise = util.promisify(glob); // Constants const SRC_DIR = './src'; -const BUILD_DIR = './build'; -const TEMP_DIR = path.join(BUILD_DIR, 'temp'); const MD_FILES_DIR = path.join('./docs', 'API-Reference'); -const TEMP_CHECK_DIR = path.join(BUILD_DIR, 'check_copy'); const BATCH_SIZE = 12; @@ -26,14 +23,6 @@ async function createDir(dirPath) { } -/** - * Remove directory - * @param {string} dirPath - The path to the directory to remove - */ -async function removeDir(dirPath) { - await fs.rm(dirPath, { recursive: true, force: true }); -} - /** * Responsible to extract file names with their parent dir * Ex :- docs/API-Reference/worker/WorkerComm -> worker/WorkerComm @@ -201,25 +190,6 @@ function normalizeLineEndings(content) { } -/** - * Compare two files based on their MD5 hash values - * @param {string} file1 - Path to the first file - * @param {string} file2 - Path to the second file - * @returns {Promise} - True if files are different, false otherwise - */ -async function areFilesDifferent(file1, file2) { - const [content1, content2] = await Promise.all([ - fs.readFile(file1, 'utf-8'), - fs.readFile(file2, 'utf-8') - ]); - - const hash1 = crypto.createHash('md5').update(content1).digest('hex'); - const hash2 = crypto.createHash('md5').update(content2).digest('hex'); - - return hash1 !== hash2; -} - - /** * Generates markdown documentation for a given JavaScript file * @param {string} file Path to the JavaScript file @@ -230,54 +200,40 @@ async function generateMarkdown(file, relativePath) { const fileName = path.basename(file, '.js'); const modifiedContent = modifyJs(content, fileName); - await fs.writeFile(file, modifiedContent, 'utf-8'); + + // Generate markdown using jsdoc-to-markdown as a library + const markdownContent = await jsdoc2md.render({ source: modifiedContent }); + const newContent = normalizeLineEndings( + modifyMarkdown(markdownContent, path.join(relativePath, fileName)) + ); const outputDir = path.join(MD_FILES_DIR, relativePath); await createDir(outputDir); const outputFileName = path.join(outputDir, `${fileName}.md`); - const tempOutputFileName = path.join( - TEMP_CHECK_DIR, `${fileName}_temp.md` - ); - - await createDir(TEMP_CHECK_DIR); - // Generate markdown to a temporary file - await exec(`npx jsdoc-to-markdown ${file} > ${tempOutputFileName}`); - - let markdownContent = await fs.readFile(tempOutputFileName, 'utf-8'); - const updatedMarkdownContent = modifyMarkdown( - markdownContent, path.join(relativePath, fileName) - ); - - await fs.writeFile(tempOutputFileName, normalizeLineEndings(updatedMarkdownContent), 'utf-8'); - - const fileExists = await fs.access(outputFileName).then(() => true).catch( - () => false - ); + // Compare with existing file in memory to avoid unnecessary writes + let existingContent = null; + try { + existingContent = await fs.readFile(outputFileName, 'utf-8'); + } catch (e) { + // File doesn't exist yet + } - const shouldUpdate = !fileExists || await areFilesDifferent( - outputFileName, tempOutputFileName - ); + const newHash = crypto.createHash('md5').update(newContent).digest('hex'); + const existingHash = existingContent + ? crypto.createHash('md5').update(existingContent).digest('hex') + : null; - if (shouldUpdate) { - await fs.rename(tempOutputFileName, outputFileName); + if (newHash !== existingHash) { + await fs.writeFile(outputFileName, newContent, 'utf-8'); console.log(`Updated ${outputFileName}`); } else { - await fs.unlink(tempOutputFileName); console.log(`No changes in ${outputFileName}`); } } -/** - * Cleans up temp directories - */ -async function cleanupTempDir() { - await removeDir(TEMP_CHECK_DIR); -} - - /** * Driver function */ @@ -288,7 +244,6 @@ async function driver() { await getExistingMarkdownFiles(jsFiles); console.log(`Found ${jsFiles.length} files to process`); - await createDir(TEMP_DIR); await createDir(MD_FILES_DIR); for (let i = 0; i < jsFiles.length; i += BATCH_SIZE) { @@ -297,25 +252,14 @@ async function driver() { const relativePath = path.relative( SRC_DIR, path.dirname(file) ); - const tempDirPath = path.join(TEMP_DIR, relativePath); - await createDir(tempDirPath); - - const fileName = path.basename(file); - const destPath = path.join(tempDirPath, fileName); - await fs.copyFile(file, destPath); - - await generateMarkdown(destPath, relativePath); + await generateMarkdown(file, relativePath); console.log(`Processed ${file}`); })); } - await removeDir(TEMP_DIR); - await cleanupTempDir(); console.log("All files processed successfully!"); } catch (error) { console.error("An error occurred:", error); - await removeDir(TEMP_DIR).catch(() => { }); - await cleanupTempDir().catch(() => { }); } }