├── .github └── demo.gif ├── .gitignore ├── LICENSE.md ├── README.md ├── keymaps └── copy-with-syntax.cson ├── lib ├── copy-with-syntax.js ├── main.js └── rtf.js ├── menus └── copy-with-syntax.cson └── package.json /.github/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frantic/copy-with-syntax/1e6040f9a8ceb503e3726d2f8f1f52e6b4eff0fb/.github/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Alex Kotliarskyi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # copy-with-syntax package 2 | 3 | Buggy hack that sometimes can copy visible content from Atom text buffer into clipboard preserving fonts and colors. 4 | 5 | ![Demo](https://raw.githubusercontent.com/frantic/copy-with-syntax/master/.github/demo.gif) 6 | -------------------------------------------------------------------------------- /keymaps/copy-with-syntax.cson: -------------------------------------------------------------------------------- 1 | 'atom-workspace': 2 | 'ctrl-alt-c': 'copy-with-syntax:copy' 3 | -------------------------------------------------------------------------------- /lib/copy-with-syntax.js: -------------------------------------------------------------------------------- 1 | var RTF = require('./rtf'); 2 | var copy = require('copy-paste'); 3 | 4 | function copyWithSyntax() { 5 | // It's a bit of a hack, but we need to find the div that 6 | // contains all lines inside text editor 7 | var textEditor = atom.workspace.getActiveTextEditor(); 8 | var el = atom.workspace.viewRegistry.getView(textEditor); 9 | var lines = findEditorDOM(el.shadowRoot); 10 | if (!lines) { 11 | throw new Error('Cound not locate lines DOM inside editor'); 12 | } 13 | 14 | var rtf = RTF(window.getComputedStyle(lines).fontFamily); 15 | forEachTextNodeIn(lines, (node) => { 16 | if (!node) { 17 | return rtf.append('\n'); 18 | } 19 | if (!node.data) { 20 | return; 21 | } 22 | 23 | var style = window.getComputedStyle(node.parentElement); 24 | rtf.append(node.data, style.color, { 25 | bold: style.fontWeight === 'bold', 26 | underline: style.textDecoration === 'underline', 27 | italic: style.fontStyle === 'italic', 28 | }); 29 | }); 30 | 31 | copy.copy(rtf.finalize()); 32 | } 33 | 34 | function forEachTextNodeIn(root, f) { 35 | if (root.nodeName === '#text') { 36 | return f(root); 37 | } 38 | var childNodes = [].concat.apply([], root.childNodes); 39 | if (childNodes && childNodes[0] && childNodes[0].style && childNodes[0].style.zIndex) { 40 | // At the time of writing this plugin, Atom splits text inside the editor 41 | // into several absolutely-positioned divs with z-index corresponding to 42 | // visual order 43 | childNodes.sort((a, b) => (b.style.zIndex - a.style.zIndex)); 44 | } 45 | for (var i = 0; i < childNodes.length; i++) { 46 | forEachTextNodeIn(childNodes[i], f); 47 | } 48 | if (root.classList.contains('line')) { 49 | f(null); 50 | } 51 | } 52 | 53 | function findEditorDOM(root) { 54 | if (root.classList && root.classList.contains('lines')) { 55 | return root; 56 | } 57 | for (var i = 0; i < root.children.length; i++) { 58 | var element = findEditorDOM(root.children[i]); 59 | if (element) { 60 | return element; 61 | } 62 | } 63 | } 64 | 65 | module.exports = copyWithSyntax; 66 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | var CompositeDisposable = require('atom').CompositeDisposable; 2 | var copyWithSyntax = require('./copy-with-syntax'); 3 | 4 | var subscriptions; 5 | 6 | module.exports = { 7 | activate() { 8 | subscriptions = new CompositeDisposable(); 9 | subscriptions.add(atom.commands.add('atom-workspace', 'copy-with-syntax:copy', copyWithSyntax)); 10 | }, 11 | 12 | deactivate() { 13 | subscriptions.dispose(); 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /lib/rtf.js: -------------------------------------------------------------------------------- 1 | function rgbaToRTF(color) { 2 | var match = color.match(/rgba?\((\d+), (\d+), (\d+).*/); 3 | var red = match ? match[1] : 0; 4 | var green = match ? match[2] : 0; 5 | var blue = match ? match[3] : 0; 6 | return `\\red${red}\\green${green}\\blue${blue}`; 7 | } 8 | 9 | module.exports = function RTF(fontFamily) { 10 | var colors = []; 11 | var lastColor = null; 12 | var content = ''; 13 | var font = fontFamily.replace(/[' ]/g, ''); 14 | 15 | function colorIndexFromTable(color) { 16 | var index = colors.indexOf(color); 17 | if (index === -1) { 18 | index = colors.push(color) - 1; 19 | } 20 | return index + 1; // 1-based index 21 | } 22 | 23 | return { 24 | append(text, color, style) { 25 | if (color && color !== lastColor) { 26 | content += '\\cf' + colorIndexFromTable(color) + ' '; 27 | lastColor = color; 28 | } 29 | text = text 30 | .replace(/[\\{\}\~]/g, '\\$&') 31 | .replace(/\n\r/g,' \\line ') 32 | .replace(/\n/g,' \\line ') 33 | .replace(/\r/g,' \\line '); 34 | 35 | style = style || {}; 36 | if (style.bold) { 37 | text = `\\b ${text}\\b0 `; 38 | } 39 | if (style.underline) { 40 | text = `\\ul ${text}\\ul0 `; 41 | } 42 | if (style.italic) { 43 | text = `\\i ${text}\\i0 `; 44 | } 45 | content += text; 46 | }, 47 | 48 | finalize() { 49 | var colortbl = colors.map(rgbaToRTF).join(';'); 50 | var fonttbl = ['Regular', 'Bold', 'Italic'].map( 51 | (flavor, ii) => `\\f${ii}\\fnil\\fcharset0 ${font}-${flavor};` 52 | ).join(''); 53 | 54 | return [ 55 | '{\\rtf1\\ansi\\ansicpg1252', 56 | `{\\fonttbl${fonttbl}}`, 57 | `{\\colortbl;${colortbl};}`, 58 | `\\f0\\fs24`, 59 | content, 60 | '}', 61 | ].join('\n'); 62 | } 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /menus/copy-with-syntax.cson: -------------------------------------------------------------------------------- 1 | 'menu': [ 2 | { 3 | 'label': 'Edit' 4 | 'submenu': [ 5 | 'label': 'Copy content with syntax' 6 | 'command': 'copy-with-syntax:copy' 7 | ] 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copy-with-syntax", 3 | "main": "./lib/main", 4 | "version": "0.0.3", 5 | "description": "Copy buffer content with syntax highlighting", 6 | "keywords": [ 7 | "copy", 8 | "formatting", 9 | "colors", 10 | "syntax", 11 | "highlighting" 12 | ], 13 | "activationCommands": { 14 | "atom-workspace": "copy-with-syntax:copy" 15 | }, 16 | "repository": "https://github.com/frantic/copy-with-syntax", 17 | "license": "MIT", 18 | "engines": { 19 | "atom": ">=1.0.0 <2.0.0" 20 | }, 21 | "dependencies": { 22 | "copy-paste": "^1.1.4" 23 | } 24 | } 25 | --------------------------------------------------------------------------------