From c042cfba6b77a69784d29a2e2a4a9f06081c3df5 Mon Sep 17 00:00:00 2001 From: Laura Nervo <laura.nervo@esrf.fr> Date: Sun, 9 Feb 2014 18:34:44 +0100 Subject: [PATCH] cprintf : added useful function to display styled formatted text in the command window. Signed-off-by: Laura Nervo <laura.nervo@esrf.fr> --- zUtil_Help/cprintf/cprintf.m | 554 +++++++++++++++++++++++++++++++++ zUtil_Help/cprintf/license.txt | 24 ++ 2 files changed, 578 insertions(+) create mode 100644 zUtil_Help/cprintf/cprintf.m create mode 100644 zUtil_Help/cprintf/license.txt diff --git a/zUtil_Help/cprintf/cprintf.m b/zUtil_Help/cprintf/cprintf.m new file mode 100644 index 00000000..335b098f --- /dev/null +++ b/zUtil_Help/cprintf/cprintf.m @@ -0,0 +1,554 @@ +function count = cprintf(style,format,varargin) +% CPRINTF displays styled formatted text in the Command Window +% +% Syntax: +% count = cprintf(style,format,...) +% +% Description: +% CPRINTF processes the specified text using the exact same FORMAT +% arguments accepted by the built-in SPRINTF and FPRINTF functions. +% +% CPRINTF then displays the text in the Command Window using the +% specified STYLE argument. The accepted styles are those used for +% Matlab's syntax highlighting (see: File / Preferences / Colors / +% M-file Syntax Highlighting Colors), and also user-defined colors. +% +% The possible pre-defined STYLE names are: +% +% 'Text' - default: black +% 'Keywords' - default: blue +% 'Comments' - default: green +% 'Strings' - default: purple +% 'UnterminatedStrings' - default: dark red +% 'SystemCommands' - default: orange +% 'Errors' - default: light red +% 'Hyperlinks' - default: underlined blue +% +% 'Black','Cyan','Magenta','Blue','Green','Red','Yellow','White' +% +% STYLE beginning with '-' or '_' will be underlined. For example: +% '-Blue' is underlined blue, like 'Hyperlinks'; +% '_Comments' is underlined green etc. +% +% STYLE beginning with '*' will be bold (R2011b+ only). For example: +% '*Blue' is bold blue; +% '*Comments' is bold green etc. +% Note: Matlab does not currently support both bold and underline, +% only one of them can be used in a single cprintf command. But of +% course bold and underline can be mixed by using separate commands. +% +% STYLE also accepts a regular Matlab RGB vector, that can be underlined +% and bolded: -[0,1,1] means underlined cyan, '*[1,0,0]' is bold red. +% +% STYLE is case-insensitive and accepts unique partial strings just +% like handle property names. +% +% CPRINTF by itself, without any input parameters, displays a demo +% +% Example: +% cprintf; % displays the demo +% cprintf('text', 'regular black text'); +% cprintf('hyper', 'followed %s','by'); +% cprintf('key', '%d colored', 4); +% cprintf('-comment','& underlined'); +% cprintf('err', 'elements\n'); +% cprintf('cyan', 'cyan'); +% cprintf('_green', 'underlined green'); +% cprintf(-[1,0,1], 'underlined magenta'); +% cprintf([1,0.5,0],'and multi-\nline orange\n'); +% cprintf('*blue', 'and *bold* (R2011b+ only)\n'); +% cprintf('string'); % same as fprintf('string') and cprintf('text','string') +% +% Bugs and suggestions: +% Please send to Yair Altman (altmany at gmail dot com) +% +% Warning: +% This code heavily relies on undocumented and unsupported Matlab +% functionality. It works on Matlab 7+, but use at your own risk! +% +% A technical description of the implementation can be found at: +% <a href="http://undocumentedmatlab.com/blog/cprintf/">http://UndocumentedMatlab.com/blog/cprintf/</a> +% +% Limitations: +% 1. In R2011a and earlier, a single space char is inserted at the +% beginning of each CPRINTF text segment (this is ok in R2011b+). +% +% 2. In R2011a and earlier, consecutive differently-colored multi-line +% CPRINTFs sometimes display incorrectly on the bottom line. +% As far as I could tell this is due to a Matlab bug. Examples: +% >> cprintf('-str','under\nline'); cprintf('err','red\n'); % hidden 'red', unhidden '_' +% >> cprintf('str','regu\nlar'); cprintf('err','red\n'); % underline red (not purple) 'lar' +% +% 3. Sometimes, non newline ('\n')-terminated segments display unstyled +% (black) when the command prompt chevron ('>>') regains focus on the +% continuation of that line (I can't pinpoint when this happens). +% To fix this, simply newline-terminate all command-prompt messages. +% +% 4. In R2011b and later, the above errors appear to be fixed. However, +% the last character of an underlined segment is not underlined for +% some unknown reason (add an extra space character to make it look better) +% +% 5. In old Matlab versions (e.g., Matlab 7.1 R14), multi-line styles +% only affect the first line. Single-line styles work as expected. +% R14 also appends a single space after underlined segments. +% +% 6. Bold style is only supported on R2011b+, and cannot also be underlined. +% +% Change log: +% 2012-08-09: Graceful degradation support for deployed (compiled) and non-desktop applications; minor bug fixes +% 2012-08-06: Fixes for R2012b; added bold style; accept RGB string (non-numeric) style +% 2011-11-27: Fixes for R2011b +% 2011-08-29: Fix by Danilo (FEX comment) for non-default text colors +% 2011-03-04: Performance improvement +% 2010-06-27: Fix for R2010a/b; fixed edge case reported by Sharron; CPRINTF with no args runs the demo +% 2009-09-28: Fixed edge-case problem reported by Swagat K +% 2009-05-28: corrected nargout behavior sugegsted by Andreas Gäb +% 2009-05-13: First version posted on <a href="http://www.mathworks.com/matlabcentral/fileexchange/authors/27420">MathWorks File Exchange</a> +% +% See also: +% sprintf, fprintf + +% License to use and modify this code is granted freely to all interested, as long as the original author is +% referenced and attributed as such. The original author maintains the right to be solely associated with this work. + +% Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com +% $Revision: 1.08 $ $Date: 2012/10/17 21:41:09 $ + + persistent majorVersion minorVersion + if isempty(majorVersion) + %v = version; if str2double(v(1:3)) <= 7.1 + %majorVersion = str2double(regexprep(version,'^(\d+).*','$1')); + %minorVersion = str2double(regexprep(version,'^\d+\.(\d+).*','$1')); + %[a,b,c,d,versionIdStrs]=regexp(version,'^(\d+)\.(\d+).*'); %#ok unused + v = sscanf(version, '%d.', 2); + majorVersion = v(1); %str2double(versionIdStrs{1}{1}); + minorVersion = v(2); %str2double(versionIdStrs{1}{2}); + end + + % The following is for debug use only: + %global docElement txt el + if ~exist('el','var') || isempty(el), el=handle([]); end %#ok mlint short-circuit error ("used before defined") + if nargin<1, showDemo(majorVersion,minorVersion); return; end + if isempty(style), return; end + if all(ishandle(style)) && length(style)~=3 + dumpElement(style); + return; + end + + % Process the text string + if nargin<2, format = style; style='text'; end + %error(nargchk(2, inf, nargin, 'struct')); + %str = sprintf(format,varargin{:}); + + % In compiled mode + try useDesktop = usejava('desktop'); catch, useDesktop = false; end + if isdeployed | ~useDesktop %#ok<OR2> - for Matlab 6 compatibility + % do not display any formatting - use simple fprintf() + % See: http://undocumentedmatlab.com/blog/bold-color-text-in-the-command-window/#comment-103035 + % Also see: https://mail.google.com/mail/u/0/?ui=2&shva=1#all/1390a26e7ef4aa4d + % Also see: https://mail.google.com/mail/u/0/?ui=2&shva=1#all/13a6ed3223333b21 + count1 = fprintf(format,varargin{:}); + else + % Else (Matlab desktop mode) + % Get the normalized style name and underlining flag + [underlineFlag, boldFlag, style] = processStyleInfo(style); + + % Set hyperlinking, if so requested + if underlineFlag + format = ['<a href="">' format '</a>']; + + % Matlab 7.1 R14 (possibly a few newer versions as well?) + % have a bug in rendering consecutive hyperlinks + % This is fixed by appending a single non-linked space + if majorVersion < 7 || (majorVersion==7 && minorVersion <= 1) + format(end+1) = ' '; + end + end + + % Set bold, if requested and supported (R2011b+) + if boldFlag + if (majorVersion > 7 || minorVersion >= 13) + format = ['<strong>' format '</strong>']; + else + boldFlag = 0; + end + end + + % Get the current CW position + cmdWinDoc = com.mathworks.mde.cmdwin.CmdWinDocument.getInstance; + lastPos = cmdWinDoc.getLength; + + % If not beginning of line + bolFlag = 0; %#ok + %if docElement.getEndOffset - docElement.getStartOffset > 1 + % Display a hyperlink element in order to force element separation + % (otherwise adjacent elements on the same line will be merged) + if majorVersion<7 || (majorVersion==7 && minorVersion<13) + if ~underlineFlag + fprintf('<a href=""> </a>'); %fprintf('<a href=""> </a>\b'); + elseif format(end)~=10 % if no newline at end + fprintf(' '); %fprintf(' \b'); + end + end + %drawnow; + bolFlag = 1; + %end + + % Get a handle to the Command Window component + mde = com.mathworks.mde.desk.MLDesktop.getInstance; + cw = mde.getClient('Command Window'); + xCmdWndView = cw.getComponent(0).getViewport.getComponent(0); + + % Store the CW background color as a special color pref + % This way, if the CW bg color changes (via File/Preferences), + % it will also affect existing rendered strs + com.mathworks.services.Prefs.setColorPref('CW_BG_Color',xCmdWndView.getBackground); + + % Display the text in the Command Window + count1 = fprintf(2,format,varargin{:}); + + %awtinvoke(cmdWinDoc,'remove',lastPos,1); % TODO: find out how to remove the extra '_' + drawnow; % this is necessary for the following to work properly (refer to Evgeny Pr in FEX comment 16/1/2011) + docElement = cmdWinDoc.getParagraphElement(lastPos+1); + if majorVersion<7 || (majorVersion==7 && minorVersion<13) + if bolFlag && ~underlineFlag + % Set the leading hyperlink space character ('_') to the bg color, effectively hiding it + % Note: old Matlab versions have a bug in hyperlinks that need to be accounted for... + %disp(' '); dumpElement(docElement) + setElementStyle(docElement,'CW_BG_Color',1+underlineFlag,majorVersion,minorVersion); %+getUrlsFix(docElement)); + %disp(' '); dumpElement(docElement) + el(end+1) = handle(docElement); %#ok used in debug only + end + + % Fix a problem with some hidden hyperlinks becoming unhidden... + fixHyperlink(docElement); + %dumpElement(docElement); + end + + % Get the Document Element(s) corresponding to the latest fprintf operation + while docElement.getStartOffset < cmdWinDoc.getLength + % Set the element style according to the current style + %disp(' '); dumpElement(docElement) + specialFlag = underlineFlag | boldFlag; + setElementStyle(docElement,style,specialFlag,majorVersion,minorVersion); + %disp(' '); dumpElement(docElement) + docElement2 = cmdWinDoc.getParagraphElement(docElement.getEndOffset+1); + if isequal(docElement,docElement2), break; end + docElement = docElement2; + %disp(' '); dumpElement(docElement) + end + + % Force a Command-Window repaint + % Note: this is important in case the rendered str was not '\n'-terminated + xCmdWndView.repaint; + + % The following is for debug use only: + el(end+1) = handle(docElement); %#ok used in debug only + %elementStart = docElement.getStartOffset; + %elementLength = docElement.getEndOffset - elementStart; + %txt = cmdWinDoc.getText(elementStart,elementLength); + end + + if nargout + count = count1; + end + return; % debug breakpoint + +% Process the requested style information +function [underlineFlag,boldFlag,style] = processStyleInfo(style) + underlineFlag = 0; + boldFlag = 0; + + % First, strip out the underline/bold markers + if ischar(style) + % Styles containing '-' or '_' should be underlined (using a no-target hyperlink hack) + %if style(1)=='-' + underlineIdx = (style=='-') | (style=='_'); + if any(underlineIdx) + underlineFlag = 1; + %style = style(2:end); + style = style(~underlineIdx); + end + + % Check for bold style (only if not underlined) + boldIdx = (style=='*'); + if any(boldIdx) + boldFlag = 1; + style = style(~boldIdx); + end + if underlineFlag && boldFlag + warning('YMA:cprintf:BoldUnderline','Matlab does not support both bold & underline') + end + + % Check if the remaining style sting is a numeric vector + %styleNum = str2num(style); %#ok<ST2NM> % not good because style='text' is evaled! + %if ~isempty(styleNum) + if any(style==' ' | style==',' | style==';') + style = str2num(style); %#ok<ST2NM> + end + end + + % Style = valid matlab RGB vector + if isnumeric(style) && length(style)==3 && all(style<=1) && all(abs(style)>=0) + if any(style<0) + underlineFlag = 1; + style = abs(style); + end + style = getColorStyle(style); + + elseif ~ischar(style) + error('YMA:cprintf:InvalidStyle','Invalid style - see help section for a list of valid style values') + + % Style name + else + % Try case-insensitive partial/full match with the accepted style names + validStyles = {'Text','Keywords','Comments','Strings','UnterminatedStrings','SystemCommands','Errors', ... + 'Black','Cyan','Magenta','Blue','Green','Red','Yellow','White', ... + 'Hyperlinks'}; + matches = find(strncmpi(style,validStyles,length(style))); + + % No match - error + if isempty(matches) + error('YMA:cprintf:InvalidStyle','Invalid style - see help section for a list of valid style values') + + % Too many matches (ambiguous) - error + elseif length(matches) > 1 + error('YMA:cprintf:AmbigStyle','Ambiguous style name - supply extra characters for uniqueness') + + % Regular text + elseif matches == 1 + style = 'ColorsText'; % fixed by Danilo, 29/8/2011 + + % Highlight preference style name + elseif matches < 8 + style = ['Colors_M_' validStyles{matches}]; + + % Color name + elseif matches < length(validStyles) + colors = [0,0,0; 0,1,1; 1,0,1; 0,0,1; 0,1,0; 1,0,0; 1,1,0; 1,1,1]; + requestedColor = colors(matches-7,:); + style = getColorStyle(requestedColor); + + % Hyperlink + else + style = 'Colors_HTML_HTMLLinks'; % CWLink + underlineFlag = 1; + end + end + +% Convert a Matlab RGB vector into a known style name (e.g., '[255,37,0]') +function styleName = getColorStyle(rgb) + intColor = int32(rgb*255); + javaColor = java.awt.Color(intColor(1), intColor(2), intColor(3)); + styleName = sprintf('[%d,%d,%d]',intColor); + com.mathworks.services.Prefs.setColorPref(styleName,javaColor); + +% Fix a bug in some Matlab versions, where the number of URL segments +% is larger than the number of style segments in a doc element +function delta = getUrlsFix(docElement) %#ok currently unused + tokens = docElement.getAttribute('SyntaxTokens'); + links = docElement.getAttribute('LinkStartTokens'); + if length(links) > length(tokens(1)) + delta = length(links) > length(tokens(1)); + else + delta = 0; + end + +% fprintf(2,str) causes all previous '_'s in the line to become red - fix this +function fixHyperlink(docElement) + try + tokens = docElement.getAttribute('SyntaxTokens'); + urls = docElement.getAttribute('HtmlLink'); + urls = urls(2); + links = docElement.getAttribute('LinkStartTokens'); + offsets = tokens(1); + styles = tokens(2); + doc = docElement.getDocument; + + % Loop over all segments in this docElement + for idx = 1 : length(offsets)-1 + % If this is a hyperlink with no URL target and starts with ' ' and is collored as an error (red)... + if strcmp(styles(idx).char,'Colors_M_Errors') + character = char(doc.getText(offsets(idx)+docElement.getStartOffset,1)); + if strcmp(character,' ') + if isempty(urls(idx)) && links(idx)==0 + % Revert the style color to the CW background color (i.e., hide it!) + styles(idx) = java.lang.String('CW_BG_Color'); + end + end + end + end + catch + % never mind... + end + +% Set an element to a particular style (color) +function setElementStyle(docElement,style,specialFlag, majorVersion,minorVersion) + %global tokens links urls urlTargets % for debug only + global oldStyles + if nargin<3, specialFlag=0; end + % Set the last Element token to the requested style: + % Colors: + tokens = docElement.getAttribute('SyntaxTokens'); + try + styles = tokens(2); + oldStyles{end+1} = styles.cell; + + % Correct edge case problem + extraInd = double(majorVersion>7 || (majorVersion==7 && minorVersion>=13)); % =0 for R2011a-, =1 for R2011b+ + %{ + if ~strcmp('CWLink',char(styles(end-hyperlinkFlag))) && ... + strcmp('CWLink',char(styles(end-hyperlinkFlag-1))) + extraInd = 0;%1; + end + hyperlinkFlag = ~isempty(strmatch('CWLink',tokens(2))); + hyperlinkFlag = 0 + any(cellfun(@(c)(~isempty(c)&&strcmp(c,'CWLink')),tokens(2).cell)); + %} + + styles(end-extraInd) = java.lang.String(''); + styles(end-extraInd-specialFlag) = java.lang.String(style); %#ok apparently unused but in reality used by Java + if extraInd + styles(end-specialFlag) = java.lang.String(style); + end + + oldStyles{end} = [oldStyles{end} styles.cell]; + catch + % never mind for now + end + + % Underlines (hyperlinks): + %{ + links = docElement.getAttribute('LinkStartTokens'); + if isempty(links) + %docElement.addAttribute('LinkStartTokens',repmat(int32(-1),length(tokens(2)),1)); + else + %TODO: remove hyperlink by setting the value to -1 + end + %} + + % Correct empty URLs to be un-hyperlinkable (only underlined) + urls = docElement.getAttribute('HtmlLink'); + if ~isempty(urls) + urlTargets = urls(2); + for urlIdx = 1 : length(urlTargets) + try + if urlTargets(urlIdx).length < 1 + urlTargets(urlIdx) = []; % '' => [] + end + catch + % never mind... + a=1; %#ok used for debug breakpoint... + end + end + end + + % Bold: (currently unused because we cannot modify this immutable int32 numeric array) + %{ + try + %hasBold = docElement.isDefined('BoldStartTokens'); + bolds = docElement.getAttribute('BoldStartTokens'); + if ~isempty(bolds) + %docElement.addAttribute('BoldStartTokens',repmat(int32(1),length(bolds),1)); + end + catch + % never mind - ignore... + a=1; %#ok used for debug breakpoint... + end + %} + + return; % debug breakpoint + +% Display information about element(s) +function dumpElement(docElements) + %return; + numElements = length(docElements); + cmdWinDoc = docElements(1).getDocument; + for elementIdx = 1 : numElements + if numElements > 1, fprintf('Element #%d:\n',elementIdx); end + docElement = docElements(elementIdx); + if ~isjava(docElement), docElement = docElement.java; end + %docElement.dump(java.lang.System.out,1) + disp(' '); + disp(docElement) + tokens = docElement.getAttribute('SyntaxTokens'); + if isempty(tokens), continue; end + links = docElement.getAttribute('LinkStartTokens'); + urls = docElement.getAttribute('HtmlLink'); + try bolds = docElement.getAttribute('BoldStartTokens'); catch, bolds = []; end + txt = {}; + tokenLengths = tokens(1); + for tokenIdx = 1 : length(tokenLengths)-1 + tokenLength = diff(tokenLengths(tokenIdx+[0,1])); + if (tokenLength < 0) + tokenLength = docElement.getEndOffset - docElement.getStartOffset - tokenLengths(tokenIdx); + end + txt{tokenIdx} = cmdWinDoc.getText(docElement.getStartOffset+tokenLengths(tokenIdx),tokenLength).char; %#ok + end + lastTokenStartOffset = docElement.getStartOffset + tokenLengths(end); + txt{end+1} = cmdWinDoc.getText(lastTokenStartOffset, docElement.getEndOffset-lastTokenStartOffset).char; %#ok + %cmdWinDoc.uiinspect + %docElement.uiinspect + txt = strrep(txt',sprintf('\n'),'\n'); + try + data = [tokens(2).cell m2c(tokens(1)) m2c(links) m2c(urls(1)) cell(urls(2)) m2c(bolds) txt]; + if elementIdx==1 + disp(' SyntaxTokens(2,1) - LinkStartTokens - HtmlLink(1,2) - BoldStartTokens - txt'); + disp(' =============================================================================='); + end + catch + try + data = [tokens(2).cell m2c(tokens(1)) m2c(links) txt]; + catch + disp([tokens(2).cell m2c(tokens(1)) txt]); + try + data = [m2c(links) m2c(urls(1)) cell(urls(2))]; + catch + % Mtlab 7.1 only has urls(1)... + data = [m2c(links) urls.cell]; + end + end + end + disp(data) + end + +% Utility function to convert matrix => cell +function cells = m2c(data) + %datasize = size(data); cells = mat2cell(data,ones(1,datasize(1)),ones(1,datasize(2))); + cells = num2cell(data); + +% Display the help and demo +function showDemo(majorVersion,minorVersion) + fprintf('cprintf displays formatted text in the Command Window.\n\n'); + fprintf('Syntax: count = cprintf(style,format,...); click <a href="matlab:help cprintf">here</a> for details.\n\n'); + url = 'http://UndocumentedMatlab.com/blog/cprintf/'; + fprintf(['Technical description: <a href="' url '">' url '</a>\n\n']); + fprintf('Demo:\n\n'); + boldFlag = majorVersion>7 || (majorVersion==7 && minorVersion>=13); + s = ['cprintf(''text'', ''regular black text'');' 10 ... + 'cprintf(''hyper'', ''followed %s'',''by'');' 10 ... + 'cprintf(''key'', ''%d colored'',' num2str(4+boldFlag) ');' 10 ... + 'cprintf(''-comment'',''& underlined'');' 10 ... + 'cprintf(''err'', ''elements:\n'');' 10 ... + 'cprintf(''cyan'', ''cyan'');' 10 ... + 'cprintf(''_green'', ''underlined green'');' 10 ... + 'cprintf(-[1,0,1], ''underlined magenta'');' 10 ... + 'cprintf([1,0.5,0], ''and multi-\nline orange\n'');' 10]; + if boldFlag + % In R2011b+ the internal bug that causes the need for an extra space + % is apparently fixed, so we must insert the sparator spaces manually... + % On the other hand, 2011b enables *bold* format + s = [s 'cprintf(''*blue'', ''and *bold* (R2011b+ only)\n'');' 10]; + s = strrep(s, ''')',' '')'); + s = strrep(s, ''',5)',' '',5)'); + s = strrep(s, '\n ','\n'); + end + disp(s); + eval(s); + + +%%%%%%%%%%%%%%%%%%%%%%%%%% TODO %%%%%%%%%%%%%%%%%%%%%%%%% +% - Fix: Remove leading space char (hidden underline '_') +% - Fix: Find workaround for multi-line quirks/limitations +% - Fix: Non-\n-terminated segments are displayed as black +% - Fix: Check whether the hyperlink fix for 7.1 is also needed on 7.2 etc. +% - Enh: Add font support \ No newline at end of file diff --git a/zUtil_Help/cprintf/license.txt b/zUtil_Help/cprintf/license.txt new file mode 100644 index 00000000..bed837ce --- /dev/null +++ b/zUtil_Help/cprintf/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2009, Yair Altman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. -- GitLab