diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index e9d6badc..6243cf5c 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -2,6 +2,7 @@ import PropTypes from 'prop-types' import React from 'react' import _ from 'lodash' import CodeMirror from 'codemirror' +import hljs from 'highlight.js' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' @@ -37,6 +38,85 @@ function translateHotkey (hotkey) { return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') } +const languageMaps = { + brainfuck: 'Brainfuck', + cpp: 'C++', + cs: 'C#', + clojure: 'Clojure', + 'clojure-repl': 'ClojureScript', + cmake: 'CMake', + coffeescript: 'CoffeeScript', + crystal: 'Crystal', + css: 'CSS', + d: 'D', + dart: 'Dart', + delphi: 'Pascal', + diff: 'Diff', + django: 'Django', + dockerfile: 'Dockerfile', + ebnf: 'EBNF', + elm: 'Elm', + erlang: 'Erlang', + 'erlang-repl': 'Erlang', + fortran: 'Fortran', + fsharp: 'F#', + gherkin: 'Gherkin', + go: 'Go', + groovy: 'Groovy', + haml: 'HAML', + haskell: 'Haskell', + haxe: 'Haxe', + http: 'HTTP', + ini: 'toml', + java: 'Java', + javascript: 'JavaScript', + json: 'JSON', + julia: 'Julia', + kotlin: 'Kotlin', + less: 'LESS', + livescript: 'LiveScript', + lua: 'Lua', + markdown: 'Markdown', + mathematica: 'Mathematica', + nginx: 'Nginx', + nsis: 'NSIS', + objectivec: 'Objective-C', + ocaml: 'Ocaml', + perl: 'Perl', + php: 'PHP', + powershell: 'PowerShell', + properties: 'Properties files', + protobuf: 'ProtoBuf', + python: 'Python', + puppet: 'Puppet', + q: 'Q', + r: 'R', + ruby: 'Ruby', + rust: 'Rust', + sas: 'SAS', + scala: 'Scala', + scheme: 'Scheme', + scss: 'SCSS', + shell: 'Shell', + smalltalk: 'Smalltalk', + sml: 'SML', + sql: 'SQL', + stylus: 'Stylus', + swift: 'Swift', + tcl: 'Tcl', + tex: 'LaTex', + typescript: 'TypeScript', + twig: 'Twig', + vbnet: 'VB.NET', + vbscript: 'VBScript', + verilog: 'Verilog', + vhdl: 'VHDL', + xml: 'HTML', + xquery: 'XQuery', + yaml: 'YAML', + elixir: 'Elixir' +} + export default class CodeEditor extends React.Component { constructor (props) { super(props) @@ -274,7 +354,11 @@ export default class CodeEditor extends React.Component { extraKeys: this.defaultKeyMap }) - this.setMode(this.props.mode) + if (!this.props.mode && this.props.value) { + this.autoDetectLanguage(this.props.value) + } else { + this.setMode(this.props.mode) + } this.editor.on('focus', this.focusHandler) this.editor.on('blur', this.blurHandler) @@ -644,7 +728,7 @@ export default class CodeEditor extends React.Component { } setMode (mode) { - let syntax = CodeMirror.findModeByName(convertModeName(mode)) + let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') this.editor.setOption('mode', syntax.mime) @@ -796,6 +880,11 @@ export default class CodeEditor extends React.Component { this.editor.replaceSelection(imageMd) } + autoDetectLanguage (content) { + const res = hljs.highlightAuto(content, Object.keys(languageMaps)) + this.setMode(languageMaps[res.language]) + } + handlePaste (editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props @@ -892,6 +981,10 @@ export default class CodeEditor extends React.Component { this.handlePasteText(editor, pastedTxt) } } + + if (!this.props.mode) { + this.autoDetectLanguage(editor.doc.getValue()) + } } handleScroll (e) { diff --git a/browser/lib/newNote.js b/browser/lib/newNote.js index 9511f847..d8ef196f 100644 --- a/browser/lib/newNote.js +++ b/browser/lib/newNote.js @@ -46,6 +46,8 @@ export function createSnippetNote (storage, folder, dispatch, location, params, tags = params.tagname.split(' ') } + const defaultLanguage = config.editor.snippetDefaultLanguage === 'Auto Detect' ? null : config.editor.snippetDefaultLanguage + return dataApi .createNote(storage, { type: 'SNIPPET_NOTE', @@ -56,7 +58,7 @@ export function createSnippetNote (storage, folder, dispatch, location, params, snippets: [ { name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] } diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index d8b5798d..12545335 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -599,12 +599,14 @@ class SnippetNoteDetail extends React.Component { } addSnippet () { - const { config } = this.props + const { config: { editor: { snippetDefaultLanguage } } } = this.props const { note } = this.state + const defaultLanguage = snippetDefaultLanguage === 'Auto Detect' ? null : snippetDefaultLanguage + note.snippets = note.snippets.concat([{ name: '', - mode: config.editor.snippetDefaultLanguage || 'text', + mode: defaultLanguage, content: '', linesHighlighted: [] }]) @@ -696,8 +698,6 @@ class SnippetNoteDetail extends React.Component { const viewList = note.snippets.map((snippet, index) => { const isActive = this.state.snippetIndex === index - let syntax = CodeMirror.findModeByName(convertModeName(snippet.mode)) - if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') return